From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([208.118.235.92]:57210) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SePeD-0003rP-HZ for qemu-devel@nongnu.org; Tue, 12 Jun 2012 07:52:40 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1SePe6-0002z4-CF for qemu-devel@nongnu.org; Tue, 12 Jun 2012 07:52:33 -0400 Received: from cantor2.suse.de ([195.135.220.15]:44468 helo=mx2.suse.de) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SePe5-0002yi-S9 for qemu-devel@nongnu.org; Tue, 12 Jun 2012 07:52:26 -0400 Message-ID: <4FD72D75.4050907@suse.de> Date: Tue, 12 Jun 2012 13:52:21 +0200 From: Alexander Graf MIME-Version: 1.0 References: <1338984323-21914-1-git-send-email-jfrei@de.ibm.com> <1338984323-21914-8-git-send-email-jfrei@de.ibm.com> In-Reply-To: <1338984323-21914-8-git-send-email-jfrei@de.ibm.com> Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Subject: Re: [Qemu-devel] [PATCH 7/8] s390: Add SCLP vt220 console support List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: Jens Freimann Cc: Cornelia Huck , Jens Freimann , Heinz Graalfs , qemu-devel On 06/06/2012 02:05 PM, Jens Freimann wrote: > From: Heinz Graalfs > > Adds console support (in vt220 mode). > In order to run qemu exploiting the SCLP's console functionality in vt220 mode > the user has to specify the following console related parameters: > > -chardev stdio,id=charconsole0 -device sclpconsole,chardev=charconsole0,id=console0 > > Signed-off-by: Heinz Graalfs > Signed-off-by: Jens Freimann > --- > hw/s390-event-facility.c | 209 ++++++++++++++++++++++++++++++++++++++++++++++ > hw/s390-event-facility.h | 8 ++ > hw/s390-sclp.c | 177 ++++++++++++++++++++++++++++++++++++++- > hw/s390-sclp.h | 22 ++++- > sysemu.h | 1 + > target-s390x/op_helper.c | 6 ++ > vl.c | 41 +++++++++ > 7 files changed, 460 insertions(+), 4 deletions(-) > > diff --git a/hw/s390-event-facility.c b/hw/s390-event-facility.c > index b8106a6..cfa5dd4 100644 > --- a/hw/s390-event-facility.c > +++ b/hw/s390-event-facility.c > @@ -16,6 +16,11 @@ > #include "s390-sclp.h" > #include "s390-event-facility.h" > > +qemu_irq sclp_read_vt220; > + > +static int size_buffer = 4096; > +static char *sclp_console_data_vt220; Globals? > + > struct SCLPDevice { > const char *name; > bool vm_running; > @@ -224,9 +229,213 @@ static TypeInfo sclp_quiesce_info = { > .class_init = sclpef_quiesce_class_init, > }; > > +/* ----------- SCLP VT220 console ------------ */ > + > +static void s390_signal_read_vt220(void *opaque, int n, int level) > +{ > + sclp_enable_signal_read_vt220(); > + sclp_service_interrupt(opaque, 0); > +} > + > +static void sclpef_set_console(SCLPEvent *event) > +{ > + if (event->id == ID_VT220) { > + sclp_read_vt220 = *qemu_allocate_irqs(s390_signal_read_vt220, > + event->evt_fac->opaque, 1); > + } > +} > + > +void sclpef_write_console_vt220(SCLPDevice *sdev, char *buf) > +{ > + DeviceState *dev; > + SCLPEventFacility *event_facility; > + static SCLPEvent *event; > + static SCLPEventClass *cons; > + > + event_facility = DO_UPCAST(SCLPEventFacility, sdev, sdev); > + > + if (!cons) { > + QTAILQ_FOREACH(dev,&event_facility->sbus.qbus.children, sibling) { > + event = (SCLPEvent *) dev; > + if (event->id == ID_VT220) { > + cons = SCLP_EVENT_GET_CLASS(event); > + assert(cons->have_data); > + break; > + } > + } > + } I don't understand the above code. Why do you have to search for anything when you're in a call to process a write? > + if (cons) { > + cons->have_data(event, (const uint8_t *)buf, strlen(buf)); > + } > +} > + > +char *sclpef_get_console_data_vt220(SCLPDevice *sdev) > +{ > + DeviceState *dev; > + SCLPEventFacility *event_facility; > + SCLPEvent *event = NULL; > + static SCLPEventClass *cons; > + > + event_facility = DO_UPCAST(SCLPEventFacility, sdev, sdev); > + > + if (!cons) { > + QTAILQ_FOREACH(dev,&event_facility->sbus.qbus.children, sibling) { > + event = (SCLPEvent *) dev; > + if (event->id == ID_VT220) { > + cons = SCLP_EVENT_GET_CLASS(event); > + assert(cons->get_data); > + break; > + } > + } > + } See above. > + if (cons) { > + return cons->get_data(); > + } > + return NULL; > +} > + > +static char *console_data_vt220(void) > +{ > + return sclp_console_data_vt220; > +} > + > +static ssize_t flush_buf(SCLPEvent *event, const uint8_t *buf, size_t len) > +{ > + SCLPConsole *scon = DO_UPCAST(SCLPConsole, event, event); > + ssize_t ret; > + > + if (!scon->chr) { > + /* If there's no backend, we can just say we consumed all data. */ > + return len; > + } > + > + ret = qemu_chr_fe_write(scon->chr, buf, len); > + > + if (ret< 0) { > + /* see virtio-console comments */ > + ret = 0; > + } > + > + return ret; > +} > + > +static void guest_open(SCLPEvent *event) > +{ > + SCLPConsole *scon = DO_UPCAST(SCLPConsole, event, event); > + > + if (!scon->chr) { > + return; > + } > + qemu_chr_fe_open(scon->chr); > +} > + > +static void guest_close(SCLPEvent *event) > +{ > + SCLPConsole *scon = DO_UPCAST(SCLPConsole, event, event); > + > + if (!scon->chr) { > + return; > + } > + qemu_chr_fe_close(scon->chr); > +} > + > +static int chr_can_read(void *opaque) > +{ > + return 1; > +} > + > +static void chr_read_vt220(void *opaque, const uint8_t *buf, int size) > +{ > + char *offset; > + > + if (!sclp_console_data_vt220) { > + size_buffer = 2 * size; Why 2*? > + sclp_console_data_vt220 = malloc(size_buffer); %s/malloc/g_malloc0/g > + } > + if (size_buffer< size + 1) { This could use a comment. > + free(sclp_console_data_vt220); > + size_buffer = 2 * size; > + sclp_console_data_vt220 = malloc(size_buffer); > + } > + offset = sclp_console_data_vt220; > + if (offset) { > + memcpy(offset, buf, size); > + offset += size; > + *offset = '\0'; How do you know you're not out of bounds? > + qemu_irq_raise(sclp_read_vt220); > + } else { > + size_buffer = 0; > + } > +} > + > +static void chr_event(void *opaque, int event) > +{ > + switch (event) { > + case CHR_EVENT_OPENED: > + if (!sclp_console_data_vt220) { > + sclp_console_data_vt220 = malloc(size_buffer); > + } > + break; > + case CHR_EVENT_CLOSED: > + break; > + } > +} > + > +static unsigned int send_mask_vt220(void) > +{ > + return SCLP_EVENT_MASK_MSG_ASCII; > +} > + > +static unsigned int receive_mask_vt220(void) > +{ > + return SCLP_EVENT_MASK_MSG_ASCII; > +} > + > +static int sclpconsole_initfn_vt220(SCLPEvent *event) > +{ > + SCLPConsole *scon = DO_UPCAST(SCLPConsole, event, event); > + > + event->id = ID_VT220; > + sclpef_set_console(event); > + if (scon->chr) { > + qemu_chr_add_handlers(scon->chr, chr_can_read, > + chr_read_vt220, chr_event, scon); > + } > + > + return 0; > +} > + > +static Property sclpconsole_properties[] = { > + DEFINE_PROP_CHR("chardev", SCLPConsole, chr), > + DEFINE_PROP_END_OF_LIST(), > +}; > + > +static void sclpconsole_class_init(ObjectClass *klass, void *data) > +{ > + DeviceClass *dc = DEVICE_CLASS(klass); > + SCLPEventClass *k = SCLP_EVENT_CLASS(klass); > + > + k->init = sclpconsole_initfn_vt220; > + k->have_data = flush_buf; > + k->guest_open = guest_open; > + k->guest_close = guest_close; > + k->get_send_mask = send_mask_vt220; > + k->get_receive_mask = receive_mask_vt220; > + k->get_data = console_data_vt220; > + dc->props = sclpconsole_properties; > +} > + > +static TypeInfo sclpconsole_info = { > + .name = "sclpconsole", > + .parent = TYPE_SCLP_EVENT, > + .instance_size = sizeof(SCLPConsole), > + .class_init = sclpconsole_class_init, > +}; > + > static void sclpef_register_types(void) > { > type_register_static(&sclp_event_facility_type_info); > type_register_static(&sclp_quiesce_info); > + type_register_static(&sclpconsole_info); > } > type_init(sclpef_register_types) > diff --git a/hw/s390-event-facility.h b/hw/s390-event-facility.h > index 40d4049..d6bde7d 100644 > --- a/hw/s390-event-facility.h > +++ b/hw/s390-event-facility.h > @@ -14,6 +14,7 @@ > #include "qemu-common.h" > > #define ID_QUIESCE SCLP_EVENT_SIGNAL_QUIESCE > +#define ID_VT220 SCLP_EVENT_ASCII_CONSOLE_DATA > > #define TYPE_SCLP_EVENT "s390-sclp-event-type" > #define SCLP_EVENT(obj) \ > @@ -34,6 +35,11 @@ typedef struct SCLPEventClass { > int (*exit)(SCLPEvent *event); > unsigned int (*get_send_mask)(void); > unsigned int (*get_receive_mask)(void); > + void (*guest_open)(SCLPEvent *event); > + void (*guest_close)(SCLPEvent *event); > + void (*guest_ready)(SCLPEvent *event); > + ssize_t (*have_data)(SCLPEvent *event, const uint8_t *buf, size_t len); > + char *(*get_data)(void); > } SCLPEventClass; > > struct SCLPEvent { > @@ -50,5 +56,7 @@ void sclpef_enable_irqs(SCLPDevice *sdev, void *opaque); > void sclpef_set_masks(void); > unsigned int sclpef_send_mask(SCLPDevice *sdev); > unsigned int sclpef_receive_mask(SCLPDevice *sdev); > +void sclpef_write_console_vt220(SCLPDevice *sdev, char *buf); > +char *sclpef_get_console_data_vt220(SCLPDevice *sdev); > > #endif > diff --git a/hw/s390-sclp.c b/hw/s390-sclp.c > index 683a709..8f45773 100644 > --- a/hw/s390-sclp.c > +++ b/hw/s390-sclp.c > @@ -11,6 +11,11 @@ > #include "hw/s390-sclp.h" > #include "hw/s390-event-facility.h" > > +/* input buffer handling */ > +#define INP_BUFFER_SIZE 4096 > +static int sclp_curr_buf_size; > +static char *sclp_input_vt220; > + > /* Host capabilites */ > static unsigned int sclp_send_mask; > static unsigned int sclp_receive_mask; > @@ -21,6 +26,7 @@ static unsigned int sclp_cp_receive_mask; > > static int quiesce; > static int event_pending; > +static int vt220; If anything, this is a machine variable, no? What is this supposed to express? > > int sclp_read_info(CPUS390XState *env, struct sccb *sccb) > { > @@ -66,6 +72,7 @@ void sclp_enable_signal_quiesce(void) > { > quiesce = 1; > event_pending = 1; > + vt220 = 0; > } > > static void sclp_set_masks(void) > @@ -81,7 +88,112 @@ static void sclp_set_masks(void) > > sclp_send_mask = sclpef_send_mask(evt_fac->sdev); > sclp_receive_mask = sclpef_receive_mask(evt_fac->sdev); > - } > +} > + > +static int signal_vt220_event(struct sccb *sccb, int *slen) > +{ > + char *p; > + > + if (!sclp_input_vt220 || !vt220) { > + return 0; > + } > + > + int l = strlen(sclp_input_vt220); > + > + if (*slen< sizeof(struct ascii_cons_data_command) + l + 1) { > + event_pending = 1; > + return 0; > + } > + p = (char *)&sccb->c.read.acd_cmd.data; > + /* first byte is hex 0 saying an ascii string follows */ > + *p++ = '\0'; > + memmove(p, sclp_input_vt220, l); > + *sclp_input_vt220 = '\0'; > + > + sccb->c.read.acd_cmd.h.length = > + cpu_to_be16(sizeof(struct ascii_cons_data_command) + l + 1); > + sccb->c.read.acd_cmd.h.type = SCLP_EVENT_ASCII_CONSOLE_DATA; > + > + vt220 = 0; > + *slen -= sizeof(struct ascii_cons_data_command) + l + 1; > + return 1; > +} > + > +static char *grow_buffer(int size) > +{ > + char *p = (char *) malloc(size); > + > + if (!p) { > + sclp_curr_buf_size = 0; > + return NULL; > + } > + memset(p, '\0', size); > + sclp_curr_buf_size = size; > + return p; > +} > + > +static int sclp_write_vt220(struct event_buffer_header *event) > +{ > + int l; > + char *msg; > + SCLPS390EventFacility *evt_fac; > + struct ascii_cons_data_command *ad = > + (struct ascii_cons_data_command *) event; > + > + assert(sclp_bus); > + > + l = event->length - sizeof(struct event_buffer_header); > + msg = (char *) malloc(l + 1); > + assert(msg); > + memset(msg, '\0', l + 1); > + memmove(msg, ad->data, l); Why the copy? Also, for such short lived data, you're probably better off using alloca. > + > + evt_fac = sclp_bus->event_facility; > + sclpef_write_console_vt220(evt_fac->sdev, msg); > + > + free(msg); > + > + return SCLP_RC_NORMAL_COMPLETION; > +} > + > +void sclp_enable_signal_read_vt220(void) > +{ > + int len; > + char *input; > + SCLPS390EventFacility *evt_fac; > + > + if (!sclp_bus) { > + return; > + } > + evt_fac = sclp_bus->event_facility; > + > + assert(evt_fac); > + > + input = sclpef_get_console_data_vt220(evt_fac->sdev); > + > + if (!input) { > + return; > + } > + > + vt220 = 1; > + quiesce = 0; > + event_pending = 1; > + len = strlen((char *) input); > + if (sclp_input_vt220 == NULL) { > + /* get new buffer */ > + sclp_input_vt220 = grow_buffer(2 * len + 1); > + } else { > + if (len>= sclp_curr_buf_size) { > + /* get larger buffer */ > + char *p = grow_buffer(2 * len + 1); > + free(sclp_input_vt220); > + sclp_input_vt220 = p; > + } > + } > + if (sclp_input_vt220) { > + strcat(sclp_input_vt220, (char *)input); > + } > +} > > int sclp_read_event_data(CPUS390XState *env, struct sccb *sccb) > { > @@ -99,7 +211,8 @@ int sclp_read_event_data(CPUS390XState *env, struct sccb *sccb) > break; > case SCLP_SELECTIVE_READ: > if (!(sclp_cp_receive_mask& be32_to_cpu(sccb->c.read.mask))) { > - sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SELECTION_MASK); > + sccb->h.response_code = > + cpu_to_be16(SCLP_RC_INVALID_SELECTION_MASK); > goto out; > } > sclp_active_selection_mask = be32_to_cpu(sccb->c.read.mask); > @@ -117,7 +230,12 @@ int sclp_read_event_data(CPUS390XState *env, struct sccb *sccb) > sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_COMPLETION); > } > } > - > + if (sclp_active_selection_mask& SCLP_EVENT_MASK_MSG_ASCII) { > + if (signal_vt220_event(sccb,&slen)) { > + sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_COMPLETION); > + sccb->h.length = cpu_to_be16(SCCB_SIZE - slen); > + } > + } > if (sccb->h.control_mask[2]& SCLP_VARIABLE_LENGTH_RESPONSE) { > sccb->h.control_mask[2]&= ~SCLP_VARIABLE_LENGTH_RESPONSE; > sccb->h.length = cpu_to_be16(SCCB_SIZE - slen); > @@ -127,6 +245,59 @@ out: > return 0; > } > > +int sclp_write_event_data(CPUS390XState *env, struct sccb *sccb) > +{ > + struct event_buffer_header *event; > + int slen; > + unsigned elen = 0; > + > + if (sccb->h.function_code != SCLP_FC_NORMAL_WRITE) { > + sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_FUNCTION); > + goto out; > + } > + if (be16_to_cpu(sccb->h.length)< 8) { > + sccb->h.response_code = cpu_to_be16(SCLP_RC_INSUFFICIENT_SCCB_LENGTH); > + goto out; > + } > + > + /* first check the sum of all events */ > + event =&sccb->c.event; > + for (slen = be16_to_cpu(sccb->h.length) - sizeof(sccb->h); > + slen> 0; slen -= elen) { > + elen = be16_to_cpu(event->length); > + if (elen< sizeof(*event) || elen> slen) { > + sccb->h.response_code = > + cpu_to_be16(SCLP_RC_EVENT_BUFFER_SYNTAX_ERROR); > + goto out; > + } > + event = (void *) event + elen; > + } > + if (slen) { > + sccb->h.response_code = cpu_to_be16(SCLP_RC_INCONSISTENT_LENGTHS); > + goto out; > + } > + > + /* the execute */ > + event =&sccb->c.event; > + for (slen = be16_to_cpu(sccb->h.length) - sizeof(sccb->h); > + slen> 0; slen -= elen) { > + elen = be16_to_cpu(event->length); > + switch (event->type) { > + case SCLP_EVENT_ASCII_CONSOLE_DATA: > + sccb->h.response_code = cpu_to_be16(sclp_write_vt220(event)); > + break; This also screams for a generic dispatcher. > + default: > + sccb->h.response_code = SCLP_RC_INVALID_FUNCTION; > + break; > + } > + event = (void *) event + elen; > + } > + sccb->h.response_code = SCLP_RC_NORMAL_COMPLETION; > + > +out: > + return 0; > +} > + > int sclp_write_event_mask(CPUS390XState *env, struct sccb *sccb) > { > /* Attention: We assume that Linux uses 4-byte masks, what it actually > diff --git a/hw/s390-sclp.h b/hw/s390-sclp.h > index f61421b..c86bca8 100644 > --- a/hw/s390-sclp.h > +++ b/hw/s390-sclp.h > @@ -7,6 +7,8 @@ > /* SCLP command codes */ > #define SCLP_CMDW_READ_SCP_INFO 0x00020001 > #define SCLP_CMDW_READ_SCP_INFO_FORCED 0x00120001 > +#define SCLP_CMD_READ_EVENT_DATA 0x00770005 > +#define SCLP_CMD_WRITE_EVENT_DATA 0x00760005 > #define SCLP_CMD_WRITE_EVENT_MASK 0x00780005 > > /* SCLP response codes */ > @@ -20,11 +22,12 @@ > #define SCLP_RC_INVALID_MASK_LENGTH 0x74f0 > > /* SCLP event types */ > +#define SCLP_EVENT_ASCII_CONSOLE_DATA 0x1a > #define SCLP_EVENT_SIGNAL_QUIESCE 0x1d > > /* SCLP event masks */ > #define SCLP_EVENT_MASK_SIGNAL_QUIESCE 0x00000008 > -#define SCLP_EVENT_MASK_MSG 0x40000000 > +#define SCLP_EVENT_MASK_MSG_ASCII 0x00000040 > > #define SCLP_UNCONDITIONAL_READ 0x00 > #define SCLP_SELECTIVE_READ 0x01 > @@ -44,6 +47,13 @@ struct write_event_mask { > uint32_t receive_mask; > } __attribute__((packed)); > > +struct mdb_header { > + uint16_t length; > + uint16_t type; > + uint32_t tag; > + uint32_t revision_code; > +} __attribute__((packed)); > + > struct event_buffer_header { > uint16_t length; > uint8_t type; > @@ -58,9 +68,15 @@ struct signal_quiesce { > uint8_t unit; > } __attribute__((packed)); > > +struct ascii_cons_data_command { > + struct event_buffer_header h; > + char data[0]; > +} __attribute__((packed)); > + > struct read_event_data { > union { > struct signal_quiesce quiesce; > + struct ascii_cons_data_command acd_cmd; > uint32_t mask; > }; > } __attribute__((packed)); > @@ -84,15 +100,19 @@ struct sccb { > struct sccb_header h; > union { > struct read_info_sccb read_info; > + struct event_buffer_header event; > struct read_event_data read; > + struct ascii_cons_data_command acd_cmd; > struct write_event_mask we_mask; > char data[SCCB_DATA_LEN]; > } c; > } __attribute__((packed)); > > void sclp_enable_signal_quiesce(void); > +void sclp_enable_signal_read_vt220(void); > int sclp_read_info(CPUS390XState *env, struct sccb *sccb); > int sclp_read_event_data(CPUS390XState *env, struct sccb *sccb); > +int sclp_write_event_data(CPUS390XState *env, struct sccb *sccb); > int sclp_write_event_mask(CPUS390XState *env, struct sccb *sccb); > void sclp_service_interrupt(CPUS390XState *env, uint32_t sccb); > > diff --git a/sysemu.h b/sysemu.h > index bc2c788..b4d399c 100644 > --- a/sysemu.h > +++ b/sysemu.h > @@ -62,6 +62,7 @@ int qemu_powerdown_requested(void); > void qemu_system_killed(int signal, pid_t pid); > void qemu_kill_report(void); > extern qemu_irq qemu_system_powerdown; > +extern qemu_irq sclp_read_vt220; > void qemu_system_reset(bool report); > > void qemu_add_exit_notifier(Notifier *notify); > diff --git a/target-s390x/op_helper.c b/target-s390x/op_helper.c > index 3e5eff4..4d49472 100644 > --- a/target-s390x/op_helper.c > +++ b/target-s390x/op_helper.c > @@ -2391,6 +2391,12 @@ int sclp_service_call(CPUS390XState *env, uint32_t sccb, uint64_t code) > case SCLP_CMDW_READ_SCP_INFO_FORCED: > r = sclp_read_info(env,&work_sccb); > break; > + case SCLP_CMD_READ_EVENT_DATA: > + r = sclp_read_event_data(env,&work_sccb); > + break; > + case SCLP_CMD_WRITE_EVENT_DATA: > + r = sclp_write_event_data(env,&work_sccb); > + break; > case SCLP_CMD_WRITE_EVENT_MASK: > r = sclp_write_event_mask(env,&work_sccb); > break; > diff --git a/vl.c b/vl.c > index 23ab3a3..aba7ab0 100644 > --- a/vl.c > +++ b/vl.c > @@ -174,6 +174,7 @@ int main(int argc, char **argv) > #define DEFAULT_RAM_SIZE 128 > > #define MAX_VIRTIO_CONSOLES 1 > +#define MAX_SCLP_CONSOLES 1 > > static const char *data_dir; > const char *bios_name = NULL; > @@ -201,6 +202,7 @@ int no_quit = 0; > CharDriverState *serial_hds[MAX_SERIAL_PORTS]; > CharDriverState *parallel_hds[MAX_PARALLEL_PORTS]; > CharDriverState *virtcon_hds[MAX_VIRTIO_CONSOLES]; > +CharDriverState *sclpcon_hds[MAX_SCLP_CONSOLES]; > int win2k_install_hack = 0; > int usb_enabled = 0; > int singlestep = 0; > @@ -274,6 +276,8 @@ static int default_floppy = 1; > static int default_cdrom = 1; > static int default_sdcard = 1; > static int default_vga = 1; > +static int default_sclpcon = 1; > +static int default_loader = 1; > > static struct { > const char *driver; > @@ -295,6 +299,8 @@ static struct { > { .driver = "isa-cirrus-vga", .flag =&default_vga }, > { .driver = "vmware-svga", .flag =&default_vga }, > { .driver = "qxl-vga", .flag =&default_vga }, > + { .driver = "s390-sclp", .flag =&default_sclpcon }, > + { .driver = "s390-ipl", .flag =&default_loader }, > }; > > static void res_free(void) > @@ -1942,6 +1948,7 @@ struct device_config { > DEV_VIRTCON, /* -virtioconsole */ > DEV_DEBUGCON, /* -debugcon */ > DEV_GDB, /* -gdb, -s */ > + DEV_SCLPCON, /* sclp console */ > } type; > const char *cmdline; > Location loc; > @@ -2058,6 +2065,36 @@ static int virtcon_parse(const char *devname) > return 0; > } > > +static int sclpcon_parse(const char *devname) > +{ > + QemuOptsList *device = qemu_find_opts("device"); > + static int index; > + char label[32]; > + QemuOpts *dev_opts; > + > + if (strcmp(devname, "none") == 0) > + return 0; Apart from that part not passing checkpatch, why do we have to reimplement this for every char device? Alex > + if (index == MAX_SCLP_CONSOLES) { > + fprintf(stderr, "qemu: too many sclp consoles\n"); > + exit(1); > + } > + > + dev_opts = qemu_opts_create(device, NULL, 0); > + qemu_opt_set(dev_opts, "driver", "sclpconsole"); > + > + snprintf(label, sizeof(label), "sclpcon%d", index); > + sclpcon_hds[index] = qemu_chr_new(label, devname, NULL); > + if (!sclpcon_hds[index]) { > + fprintf(stderr, "qemu: could not open sclp console '%s': %s\n", > + devname, strerror(errno)); > + return -1; > + } > + qemu_opt_set(dev_opts, "chardev", label); > + > + index++; > + return 0; > +} > + > static int debugcon_parse(const char *devname) > { > QemuOpts *opts; > @@ -3304,6 +3341,8 @@ int main(int argc, char **argv, char **envp) > add_device_config(DEV_SERIAL, "mon:stdio"); > } else if (default_virtcon&& default_monitor) { > add_device_config(DEV_VIRTCON, "mon:stdio"); > + } else if (default_sclpcon&& default_monitor) { > + add_device_config(DEV_SCLPCON, "mon:stdio"); > } else { > if (default_serial) > add_device_config(DEV_SERIAL, "stdio"); > @@ -3491,6 +3530,8 @@ int main(int argc, char **argv, char **envp) > exit(1); > if (foreach_device_config(DEV_VIRTCON, virtcon_parse)< 0) > exit(1); > + if (foreach_device_config(DEV_SCLPCON, sclpcon_parse)< 0) > + exit(1); > if (foreach_device_config(DEV_DEBUGCON, debugcon_parse)< 0) > exit(1); >