qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

char-mux.c (12322B)


      1 /*
      2  * QEMU System Emulator
      3  *
      4  * Copyright (c) 2003-2008 Fabrice Bellard
      5  *
      6  * Permission is hereby granted, free of charge, to any person obtaining a copy
      7  * of this software and associated documentation files (the "Software"), to deal
      8  * in the Software without restriction, including without limitation the rights
      9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     10  * copies of the Software, and to permit persons to whom the Software is
     11  * furnished to do so, subject to the following conditions:
     12  *
     13  * The above copyright notice and this permission notice shall be included in
     14  * all copies or substantial portions of the Software.
     15  *
     16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
     19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     22  * THE SOFTWARE.
     23  */
     24 
     25 #include "qemu/osdep.h"
     26 #include "qapi/error.h"
     27 #include "qemu/module.h"
     28 #include "qemu/option.h"
     29 #include "chardev/char.h"
     30 #include "sysemu/block-backend.h"
     31 #include "qapi/qapi-commands-control.h"
     32 #include "chardev-internal.h"
     33 
     34 /* MUX driver for serial I/O splitting */
     35 
     36 /*
     37  * Set to false by suspend_mux_open.  Open events are delayed until
     38  * resume_mux_open.  Usually suspend_mux_open is called before
     39  * command line processing and resume_mux_open afterwards.
     40  */
     41 static bool muxes_opened = true;
     42 
     43 /* Called with chr_write_lock held.  */
     44 static int mux_chr_write(Chardev *chr, const uint8_t *buf, int len)
     45 {
     46     MuxChardev *d = MUX_CHARDEV(chr);
     47     int ret;
     48     if (!d->timestamps) {
     49         ret = qemu_chr_fe_write(&d->chr, buf, len);
     50     } else {
     51         int i;
     52 
     53         ret = 0;
     54         for (i = 0; i < len; i++) {
     55             if (d->linestart) {
     56                 char buf1[64];
     57                 int64_t ti;
     58                 int secs;
     59 
     60                 ti = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
     61                 if (d->timestamps_start == -1) {
     62                     d->timestamps_start = ti;
     63                 }
     64                 ti -= d->timestamps_start;
     65                 secs = ti / 1000;
     66                 snprintf(buf1, sizeof(buf1),
     67                          "[%02d:%02d:%02d.%03d] ",
     68                          secs / 3600,
     69                          (secs / 60) % 60,
     70                          secs % 60,
     71                          (int)(ti % 1000));
     72                 /* XXX this blocks entire thread. Rewrite to use
     73                  * qemu_chr_fe_write and background I/O callbacks */
     74                 qemu_chr_fe_write_all(&d->chr,
     75                                       (uint8_t *)buf1, strlen(buf1));
     76                 d->linestart = 0;
     77             }
     78             ret += qemu_chr_fe_write(&d->chr, buf + i, 1);
     79             if (buf[i] == '\n') {
     80                 d->linestart = 1;
     81             }
     82         }
     83     }
     84     return ret;
     85 }
     86 
     87 static const char * const mux_help[] = {
     88     "% h    print this help\n\r",
     89     "% x    exit emulator\n\r",
     90     "% s    save disk data back to file (if -snapshot)\n\r",
     91     "% t    toggle console timestamps\n\r",
     92     "% b    send break (magic sysrq)\n\r",
     93     "% c    switch between console and monitor\n\r",
     94     "% %  sends %\n\r",
     95     NULL
     96 };
     97 
     98 int term_escape_char = 0x01; /* ctrl-a is used for escape */
     99 static void mux_print_help(Chardev *chr)
    100 {
    101     int i, j;
    102     char ebuf[15] = "Escape-Char";
    103     char cbuf[50] = "\n\r";
    104 
    105     if (term_escape_char > 0 && term_escape_char < 26) {
    106         snprintf(cbuf, sizeof(cbuf), "\n\r");
    107         snprintf(ebuf, sizeof(ebuf), "C-%c", term_escape_char - 1 + 'a');
    108     } else {
    109         snprintf(cbuf, sizeof(cbuf),
    110                  "\n\rEscape-Char set to Ascii: 0x%02x\n\r\n\r",
    111                  term_escape_char);
    112     }
    113     /* XXX this blocks entire thread. Rewrite to use
    114      * qemu_chr_fe_write and background I/O callbacks */
    115     qemu_chr_write_all(chr, (uint8_t *)cbuf, strlen(cbuf));
    116     for (i = 0; mux_help[i] != NULL; i++) {
    117         for (j = 0; mux_help[i][j] != '\0'; j++) {
    118             if (mux_help[i][j] == '%') {
    119                 qemu_chr_write_all(chr, (uint8_t *)ebuf, strlen(ebuf));
    120             } else {
    121                 qemu_chr_write_all(chr, (uint8_t *)&mux_help[i][j], 1);
    122             }
    123         }
    124     }
    125 }
    126 
    127 static void mux_chr_send_event(MuxChardev *d, int mux_nr, QEMUChrEvent event)
    128 {
    129     CharBackend *be = d->backends[mux_nr];
    130 
    131     if (be && be->chr_event) {
    132         be->chr_event(be->opaque, event);
    133     }
    134 }
    135 
    136 static void mux_chr_be_event(Chardev *chr, QEMUChrEvent event)
    137 {
    138     MuxChardev *d = MUX_CHARDEV(chr);
    139 
    140     if (d->focus != -1) {
    141         mux_chr_send_event(d, d->focus, event);
    142     }
    143 }
    144 
    145 static int mux_proc_byte(Chardev *chr, MuxChardev *d, int ch)
    146 {
    147     if (d->term_got_escape) {
    148         d->term_got_escape = 0;
    149         if (ch == term_escape_char) {
    150             goto send_char;
    151         }
    152         switch (ch) {
    153         case '?':
    154         case 'h':
    155             mux_print_help(chr);
    156             break;
    157         case 'x':
    158             {
    159                  const char *term =  "QEMU: Terminated\n\r";
    160                  qemu_chr_write_all(chr, (uint8_t *)term, strlen(term));
    161                  qmp_quit(NULL);
    162                  break;
    163             }
    164         case 's':
    165             blk_commit_all();
    166             break;
    167         case 'b':
    168             qemu_chr_be_event(chr, CHR_EVENT_BREAK);
    169             break;
    170         case 'c':
    171             assert(d->mux_cnt > 0); /* handler registered with first fe */
    172             /* Switch to the next registered device */
    173             mux_set_focus(chr, (d->focus + 1) % d->mux_cnt);
    174             break;
    175         case 't':
    176             d->timestamps = !d->timestamps;
    177             d->timestamps_start = -1;
    178             d->linestart = 0;
    179             break;
    180         }
    181     } else if (ch == term_escape_char) {
    182         d->term_got_escape = 1;
    183     } else {
    184     send_char:
    185         return 1;
    186     }
    187     return 0;
    188 }
    189 
    190 static void mux_chr_accept_input(Chardev *chr)
    191 {
    192     MuxChardev *d = MUX_CHARDEV(chr);
    193     int m = d->focus;
    194     CharBackend *be = d->backends[m];
    195 
    196     while (be && d->prod[m] != d->cons[m] &&
    197            be->chr_can_read && be->chr_can_read(be->opaque)) {
    198         be->chr_read(be->opaque,
    199                      &d->buffer[m][d->cons[m]++ & MUX_BUFFER_MASK], 1);
    200     }
    201 }
    202 
    203 static int mux_chr_can_read(void *opaque)
    204 {
    205     MuxChardev *d = MUX_CHARDEV(opaque);
    206     int m = d->focus;
    207     CharBackend *be = d->backends[m];
    208 
    209     if ((d->prod[m] - d->cons[m]) < MUX_BUFFER_SIZE) {
    210         return 1;
    211     }
    212 
    213     if (be && be->chr_can_read) {
    214         return be->chr_can_read(be->opaque);
    215     }
    216 
    217     return 0;
    218 }
    219 
    220 static void mux_chr_read(void *opaque, const uint8_t *buf, int size)
    221 {
    222     Chardev *chr = CHARDEV(opaque);
    223     MuxChardev *d = MUX_CHARDEV(opaque);
    224     int m = d->focus;
    225     CharBackend *be = d->backends[m];
    226     int i;
    227 
    228     mux_chr_accept_input(opaque);
    229 
    230     for (i = 0; i < size; i++)
    231         if (mux_proc_byte(chr, d, buf[i])) {
    232             if (d->prod[m] == d->cons[m] &&
    233                 be && be->chr_can_read &&
    234                 be->chr_can_read(be->opaque)) {
    235                 be->chr_read(be->opaque, &buf[i], 1);
    236             } else {
    237                 d->buffer[m][d->prod[m]++ & MUX_BUFFER_MASK] = buf[i];
    238             }
    239         }
    240 }
    241 
    242 void mux_chr_send_all_event(Chardev *chr, QEMUChrEvent event)
    243 {
    244     MuxChardev *d = MUX_CHARDEV(chr);
    245     int i;
    246 
    247     if (!muxes_opened) {
    248         return;
    249     }
    250 
    251     /* Send the event to all registered listeners */
    252     for (i = 0; i < d->mux_cnt; i++) {
    253         mux_chr_send_event(d, i, event);
    254     }
    255 }
    256 
    257 static void mux_chr_event(void *opaque, QEMUChrEvent event)
    258 {
    259     mux_chr_send_all_event(CHARDEV(opaque), event);
    260 }
    261 
    262 static GSource *mux_chr_add_watch(Chardev *s, GIOCondition cond)
    263 {
    264     MuxChardev *d = MUX_CHARDEV(s);
    265     Chardev *chr = qemu_chr_fe_get_driver(&d->chr);
    266     ChardevClass *cc = CHARDEV_GET_CLASS(chr);
    267 
    268     if (!cc->chr_add_watch) {
    269         return NULL;
    270     }
    271 
    272     return cc->chr_add_watch(chr, cond);
    273 }
    274 
    275 static void char_mux_finalize(Object *obj)
    276 {
    277     MuxChardev *d = MUX_CHARDEV(obj);
    278     int i;
    279 
    280     for (i = 0; i < d->mux_cnt; i++) {
    281         CharBackend *be = d->backends[i];
    282         if (be) {
    283             be->chr = NULL;
    284         }
    285     }
    286     qemu_chr_fe_deinit(&d->chr, false);
    287 }
    288 
    289 static void mux_chr_update_read_handlers(Chardev *chr)
    290 {
    291     MuxChardev *d = MUX_CHARDEV(chr);
    292 
    293     /* Fix up the real driver with mux routines */
    294     qemu_chr_fe_set_handlers_full(&d->chr,
    295                                   mux_chr_can_read,
    296                                   mux_chr_read,
    297                                   mux_chr_event,
    298                                   NULL,
    299                                   chr,
    300                                   chr->gcontext, true, false);
    301 }
    302 
    303 void mux_set_focus(Chardev *chr, int focus)
    304 {
    305     MuxChardev *d = MUX_CHARDEV(chr);
    306 
    307     assert(focus >= 0);
    308     assert(focus < d->mux_cnt);
    309 
    310     if (d->focus != -1) {
    311         mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_OUT);
    312     }
    313 
    314     d->focus = focus;
    315     chr->be = d->backends[focus];
    316     mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_IN);
    317 }
    318 
    319 static void qemu_chr_open_mux(Chardev *chr,
    320                               ChardevBackend *backend,
    321                               bool *be_opened,
    322                               Error **errp)
    323 {
    324     ChardevMux *mux = backend->u.mux.data;
    325     Chardev *drv;
    326     MuxChardev *d = MUX_CHARDEV(chr);
    327 
    328     drv = qemu_chr_find(mux->chardev);
    329     if (drv == NULL) {
    330         error_setg(errp, "mux: base chardev %s not found", mux->chardev);
    331         return;
    332     }
    333 
    334     d->focus = -1;
    335     /* only default to opened state if we've realized the initial
    336      * set of muxes
    337      */
    338     *be_opened = muxes_opened;
    339     qemu_chr_fe_init(&d->chr, drv, errp);
    340 }
    341 
    342 static void qemu_chr_parse_mux(QemuOpts *opts, ChardevBackend *backend,
    343                                Error **errp)
    344 {
    345     const char *chardev = qemu_opt_get(opts, "chardev");
    346     ChardevMux *mux;
    347 
    348     if (chardev == NULL) {
    349         error_setg(errp, "chardev: mux: no chardev given");
    350         return;
    351     }
    352     backend->type = CHARDEV_BACKEND_KIND_MUX;
    353     mux = backend->u.mux.data = g_new0(ChardevMux, 1);
    354     qemu_chr_parse_common(opts, qapi_ChardevMux_base(mux));
    355     mux->chardev = g_strdup(chardev);
    356 }
    357 
    358 /**
    359  * Called after processing of default and command-line-specified
    360  * chardevs to deliver CHR_EVENT_OPENED events to any FEs attached
    361  * to a mux chardev. This is done here to ensure that
    362  * output/prompts/banners are only displayed for the FE that has
    363  * focus when initial command-line processing/machine init is
    364  * completed.
    365  *
    366  * After this point, any new FE attached to any new or existing
    367  * mux will receive CHR_EVENT_OPENED notifications for the BE
    368  * immediately.
    369  */
    370 static void open_muxes(Chardev *chr)
    371 {
    372     /* send OPENED to all already-attached FEs */
    373     mux_chr_send_all_event(chr, CHR_EVENT_OPENED);
    374 
    375     /*
    376      * mark mux as OPENED so any new FEs will immediately receive
    377      * OPENED event
    378      */
    379     chr->be_open = 1;
    380 }
    381 
    382 void suspend_mux_open(void)
    383 {
    384     muxes_opened = false;
    385 }
    386 
    387 static int chardev_options_parsed_cb(Object *child, void *opaque)
    388 {
    389     Chardev *chr = (Chardev *)child;
    390 
    391     if (!chr->be_open && CHARDEV_IS_MUX(chr)) {
    392         open_muxes(chr);
    393     }
    394 
    395     return 0;
    396 }
    397 
    398 void resume_mux_open(void)
    399 {
    400     muxes_opened = true;
    401     object_child_foreach(get_chardevs_root(),
    402                          chardev_options_parsed_cb, NULL);
    403 }
    404 
    405 static void char_mux_class_init(ObjectClass *oc, void *data)
    406 {
    407     ChardevClass *cc = CHARDEV_CLASS(oc);
    408 
    409     cc->parse = qemu_chr_parse_mux;
    410     cc->open = qemu_chr_open_mux;
    411     cc->chr_write = mux_chr_write;
    412     cc->chr_accept_input = mux_chr_accept_input;
    413     cc->chr_add_watch = mux_chr_add_watch;
    414     cc->chr_be_event = mux_chr_be_event;
    415     cc->chr_update_read_handler = mux_chr_update_read_handlers;
    416 }
    417 
    418 static const TypeInfo char_mux_type_info = {
    419     .name = TYPE_CHARDEV_MUX,
    420     .parent = TYPE_CHARDEV,
    421     .class_init = char_mux_class_init,
    422     .instance_size = sizeof(MuxChardev),
    423     .instance_finalize = char_mux_finalize,
    424 };
    425 
    426 static void register_types(void)
    427 {
    428     type_register_static(&char_mux_type_info);
    429 }
    430 
    431 type_init(register_types);