qemu/hw/char/sclpconsole-lm.c
<<
>>
Prefs
   1/*
   2 * SCLP event types
   3 *    Operations Command - Line Mode input
   4 *    Message            - Line Mode output
   5 *
   6 * Copyright IBM, Corp. 2013
   7 *
   8 * Authors:
   9 *  Heinz Graalfs <graalfs@linux.vnet.ibm.com>
  10 *
  11 * This work is licensed under the terms of the GNU GPL, version 2 or (at your
  12 * option) any later version.  See the COPYING file in the top-level directory.
  13 *
  14 */
  15
  16#include "qemu/osdep.h"
  17#include "hw/qdev.h"
  18#include "qemu/thread.h"
  19#include "qemu/error-report.h"
  20#include "sysemu/char.h"
  21
  22#include "hw/s390x/sclp.h"
  23#include "hw/s390x/event-facility.h"
  24#include "hw/s390x/ebcdic.h"
  25
  26#define SIZE_BUFFER 4096
  27#define NEWLINE     "\n"
  28
  29typedef struct OprtnsCommand {
  30    EventBufferHeader header;
  31    MDMSU message_unit;
  32    char data[0];
  33} QEMU_PACKED OprtnsCommand;
  34
  35/* max size for line-mode data in 4K SCCB page */
  36#define SIZE_CONSOLE_BUFFER (SCCB_DATA_LEN - sizeof(OprtnsCommand))
  37
  38typedef struct SCLPConsoleLM {
  39    SCLPEvent event;
  40    CharDriverState *chr;
  41    bool echo;                  /* immediate echo of input if true        */
  42    uint32_t write_errors;      /* errors writing to char layer           */
  43    uint32_t length;            /* length of byte stream in buffer        */
  44    uint8_t buf[SIZE_CONSOLE_BUFFER];
  45} SCLPConsoleLM;
  46
  47/*
  48*  Character layer call-back functions
  49 *
  50 * Allow 1 character at a time
  51 *
  52 * Accumulate bytes from character layer in console buffer,
  53 * event_pending is set when a newline character is encountered
  54 *
  55 * The maximum command line length is limited by the maximum
  56 * space available in an SCCB. Line mode console input is sent
  57 * truncated to the guest in case it doesn't fit into the SCCB.
  58 */
  59
  60static int chr_can_read(void *opaque)
  61{
  62    SCLPConsoleLM *scon = opaque;
  63
  64    if (scon->event.event_pending) {
  65        return 0;
  66    }
  67    return 1;
  68}
  69
  70static void chr_read(void *opaque, const uint8_t *buf, int size)
  71{
  72    SCLPConsoleLM *scon = opaque;
  73
  74    assert(size == 1);
  75
  76    if (*buf == '\r' || *buf == '\n') {
  77        scon->event.event_pending = true;
  78        sclp_service_interrupt(0);
  79        return;
  80    }
  81    if (scon->length == SIZE_CONSOLE_BUFFER) {
  82        /* Eat the character, but still process CR and LF.  */
  83        return;
  84    }
  85    scon->buf[scon->length] = *buf;
  86    scon->length += 1;
  87    if (scon->echo) {
  88        qemu_chr_fe_write(scon->chr, buf, size);
  89    }
  90}
  91
  92/* functions to be called by event facility */
  93
  94static bool can_handle_event(uint8_t type)
  95{
  96    return type == SCLP_EVENT_MESSAGE || type == SCLP_EVENT_PMSGCMD;
  97}
  98
  99static unsigned int send_mask(void)
 100{
 101    return SCLP_EVENT_MASK_OP_CMD | SCLP_EVENT_MASK_PMSGCMD;
 102}
 103
 104static unsigned int receive_mask(void)
 105{
 106    return SCLP_EVENT_MASK_MSG | SCLP_EVENT_MASK_PMSGCMD;
 107}
 108
 109/*
 110 * Triggered by SCLP's read_event_data
 111 * - convert ASCII byte stream to EBCDIC and
 112 * - copy converted data into provided (SCLP) buffer
 113 */
 114static int get_console_data(SCLPEvent *event, uint8_t *buf, size_t *size,
 115                            int avail)
 116{
 117    int len;
 118
 119    SCLPConsoleLM *cons = DO_UPCAST(SCLPConsoleLM, event, event);
 120
 121    len = cons->length;
 122    /* data need to fit into provided SCLP buffer */
 123    if (len > avail) {
 124        return 1;
 125    }
 126
 127    ebcdic_put(buf, (char *)&cons->buf, len);
 128    *size = len;
 129    cons->length = 0;
 130    /* data provided and no more data pending */
 131    event->event_pending = false;
 132    qemu_notify_event();
 133    return 0;
 134}
 135
 136static int read_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr,
 137                           int *slen)
 138{
 139    int avail, rc;
 140    size_t src_len;
 141    uint8_t *to;
 142    OprtnsCommand *oc = (OprtnsCommand *) evt_buf_hdr;
 143
 144    if (!event->event_pending) {
 145        /* no data pending */
 146        return 0;
 147    }
 148
 149    to = (uint8_t *)&oc->data;
 150    avail = *slen - sizeof(OprtnsCommand);
 151    rc = get_console_data(event, to, &src_len, avail);
 152    if (rc) {
 153        /* data didn't fit, try next SCCB */
 154        return 1;
 155    }
 156
 157    oc->message_unit.mdmsu.gds_id = GDS_ID_MDSMU;
 158    oc->message_unit.mdmsu.length = cpu_to_be16(sizeof(struct MDMSU));
 159
 160    oc->message_unit.cpmsu.gds_id = GDS_ID_CPMSU;
 161    oc->message_unit.cpmsu.length =
 162        cpu_to_be16(sizeof(struct MDMSU) - sizeof(GdsVector));
 163
 164    oc->message_unit.text_command.gds_id = GDS_ID_TEXTCMD;
 165    oc->message_unit.text_command.length =
 166        cpu_to_be16(sizeof(struct MDMSU) - (2 * sizeof(GdsVector)));
 167
 168    oc->message_unit.self_def_text_message.key = GDS_KEY_SELFDEFTEXTMSG;
 169    oc->message_unit.self_def_text_message.length =
 170        cpu_to_be16(sizeof(struct MDMSU) - (3 * sizeof(GdsVector)));
 171
 172    oc->message_unit.text_message.key = GDS_KEY_TEXTMSG;
 173    oc->message_unit.text_message.length =
 174        cpu_to_be16(sizeof(GdsSubvector) + src_len);
 175
 176    oc->header.length = cpu_to_be16(sizeof(OprtnsCommand) + src_len);
 177    oc->header.type = SCLP_EVENT_OPRTNS_COMMAND;
 178    *slen = avail - src_len;
 179
 180    return 1;
 181}
 182
 183/*
 184 * Triggered by SCLP's write_event_data
 185 *  - write console data to character layer
 186 *  returns < 0 if an error occurred
 187 */
 188static int write_console_data(SCLPEvent *event, const uint8_t *buf, int len)
 189{
 190    int ret = 0;
 191    const uint8_t *buf_offset;
 192
 193    SCLPConsoleLM *scon = DO_UPCAST(SCLPConsoleLM, event, event);
 194
 195    if (!scon->chr) {
 196        /* If there's no backend, we can just say we consumed all data. */
 197        return len;
 198    }
 199
 200    buf_offset = buf;
 201    while (len > 0) {
 202        ret = qemu_chr_fe_write(scon->chr, buf, len);
 203        if (ret == 0) {
 204            /* a pty doesn't seem to be connected - no error */
 205            len = 0;
 206        } else if (ret == -EAGAIN || (ret > 0 && ret < len)) {
 207            len -= ret;
 208            buf_offset += ret;
 209        } else {
 210            len = 0;
 211        }
 212    }
 213
 214    return ret;
 215}
 216
 217static int process_mdb(SCLPEvent *event, MDBO *mdbo)
 218{
 219    int rc;
 220    int len;
 221    uint8_t buffer[SIZE_BUFFER];
 222
 223    len = be16_to_cpu(mdbo->length);
 224    len -= sizeof(mdbo->length) + sizeof(mdbo->type)
 225            + sizeof(mdbo->mto.line_type_flags)
 226            + sizeof(mdbo->mto.alarm_control)
 227            + sizeof(mdbo->mto._reserved);
 228
 229    assert(len <= SIZE_BUFFER);
 230
 231    /* convert EBCDIC SCLP contents to ASCII console message */
 232    ascii_put(buffer, mdbo->mto.message, len);
 233    rc = write_console_data(event, (uint8_t *)NEWLINE, 1);
 234    if (rc < 0) {
 235        return rc;
 236    }
 237    return write_console_data(event, buffer, len);
 238}
 239
 240static int write_event_data(SCLPEvent *event, EventBufferHeader *ebh)
 241{
 242    int len;
 243    int written;
 244    int errors = 0;
 245    MDBO *mdbo;
 246    SclpMsg *data = (SclpMsg *) ebh;
 247    SCLPConsoleLM *scon = DO_UPCAST(SCLPConsoleLM, event, event);
 248
 249    len = be16_to_cpu(data->mdb.header.length);
 250    if (len < sizeof(data->mdb.header)) {
 251        return SCLP_RC_INCONSISTENT_LENGTHS;
 252    }
 253    len -= sizeof(data->mdb.header);
 254
 255    /* first check message buffers */
 256    mdbo = data->mdb.mdbo;
 257    while (len > 0) {
 258        if (be16_to_cpu(mdbo->length) > len
 259                || be16_to_cpu(mdbo->length) == 0) {
 260            return SCLP_RC_INCONSISTENT_LENGTHS;
 261        }
 262        len -= be16_to_cpu(mdbo->length);
 263        mdbo = (void *) mdbo + be16_to_cpu(mdbo->length);
 264    }
 265
 266    /* then execute */
 267    len = be16_to_cpu(data->mdb.header.length) - sizeof(data->mdb.header);
 268    mdbo = data->mdb.mdbo;
 269    while (len > 0) {
 270        switch (be16_to_cpu(mdbo->type)) {
 271        case MESSAGE_TEXT:
 272            /* message text object */
 273            written = process_mdb(event, mdbo);
 274            if (written < 0) {
 275                /* character layer error */
 276                errors++;
 277            }
 278            break;
 279        default: /* ignore */
 280            break;
 281        }
 282        len -= be16_to_cpu(mdbo->length);
 283        mdbo = (void *) mdbo + be16_to_cpu(mdbo->length);
 284    }
 285    if (errors) {
 286        scon->write_errors += errors;
 287    }
 288    data->header.flags = SCLP_EVENT_BUFFER_ACCEPTED;
 289
 290    return SCLP_RC_NORMAL_COMPLETION;
 291}
 292
 293/* functions for live migration */
 294
 295static const VMStateDescription vmstate_sclplmconsole = {
 296    .name = "sclplmconsole",
 297    .version_id = 0,
 298    .minimum_version_id = 0,
 299    .fields = (VMStateField[]) {
 300        VMSTATE_BOOL(event.event_pending, SCLPConsoleLM),
 301        VMSTATE_UINT32(write_errors, SCLPConsoleLM),
 302        VMSTATE_UINT32(length, SCLPConsoleLM),
 303        VMSTATE_UINT8_ARRAY(buf, SCLPConsoleLM, SIZE_CONSOLE_BUFFER),
 304        VMSTATE_END_OF_LIST()
 305     }
 306};
 307
 308/* qemu object creation and initialization functions */
 309
 310/* tell character layer our call-back functions */
 311
 312static int console_init(SCLPEvent *event)
 313{
 314    static bool console_available;
 315
 316    SCLPConsoleLM *scon = DO_UPCAST(SCLPConsoleLM, event, event);
 317
 318    if (console_available) {
 319        error_report("Multiple line-mode operator consoles are not supported");
 320        return -1;
 321    }
 322    console_available = true;
 323
 324    if (scon->chr) {
 325        qemu_chr_add_handlers(scon->chr, chr_can_read, chr_read, NULL, scon);
 326    }
 327
 328    return 0;
 329}
 330
 331static int console_exit(SCLPEvent *event)
 332{
 333    return 0;
 334}
 335
 336static void console_reset(DeviceState *dev)
 337{
 338   SCLPEvent *event = SCLP_EVENT(dev);
 339   SCLPConsoleLM *scon = DO_UPCAST(SCLPConsoleLM, event, event);
 340
 341   event->event_pending = false;
 342   scon->length = 0;
 343   scon->write_errors = 0;
 344}
 345
 346static Property console_properties[] = {
 347    DEFINE_PROP_CHR("chardev", SCLPConsoleLM, chr),
 348    DEFINE_PROP_UINT32("write_errors", SCLPConsoleLM, write_errors, 0),
 349    DEFINE_PROP_BOOL("echo", SCLPConsoleLM, echo, true),
 350    DEFINE_PROP_END_OF_LIST(),
 351};
 352
 353static void console_class_init(ObjectClass *klass, void *data)
 354{
 355    DeviceClass *dc = DEVICE_CLASS(klass);
 356    SCLPEventClass *ec = SCLP_EVENT_CLASS(klass);
 357
 358    dc->props = console_properties;
 359    dc->reset = console_reset;
 360    dc->vmsd = &vmstate_sclplmconsole;
 361    ec->init = console_init;
 362    ec->exit = console_exit;
 363    ec->get_send_mask = send_mask;
 364    ec->get_receive_mask = receive_mask;
 365    ec->can_handle_event = can_handle_event;
 366    ec->read_event_data = read_event_data;
 367    ec->write_event_data = write_event_data;
 368    set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
 369}
 370
 371static const TypeInfo sclp_console_info = {
 372    .name          = "sclplmconsole",
 373    .parent        = TYPE_SCLP_EVENT,
 374    .instance_size = sizeof(SCLPConsoleLM),
 375    .class_init    = console_class_init,
 376    .class_size    = sizeof(SCLPEventClass),
 377};
 378
 379static void register_types(void)
 380{
 381    type_register_static(&sclp_console_info);
 382}
 383
 384type_init(register_types)
 385