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