Exemple #1
0
static void filter_redirector_setup(NetFilterState *nf, Error **errp)
{
    MirrorState *s = FILTER_REDIRECTOR(nf);
    Chardev *chr;

    if (!s->indev && !s->outdev) {
        error_setg(errp, "filter redirector needs 'indev' or "
                   "'outdev' at least one property set");
        return;
    } else if (s->indev && s->outdev) {
        if (!strcmp(s->indev, s->outdev)) {
            error_setg(errp, "'indev' and 'outdev' could not be same "
                       "for filter redirector");
            return;
        }
    }

    net_socket_rs_init(&s->rs, redirector_rs_finalize);

    if (s->indev) {
        chr = qemu_chr_find(s->indev);
        if (chr == NULL) {
            error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
                      "IN Device '%s' not found", s->indev);
            return;
        }

        if (!qemu_chr_fe_init(&s->chr_in, chr, errp)) {
            return;
        }

        qemu_chr_fe_set_handlers(&s->chr_in, redirector_chr_can_read,
                                 redirector_chr_read, redirector_chr_event,
                                 nf, NULL, true);
    }

    if (s->outdev) {
        chr = qemu_chr_find(s->outdev);
        if (chr == NULL) {
            error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
                      "OUT Device '%s' not found", s->outdev);
            return;
        }
        if (!qemu_chr_fe_init(&s->chr_out, chr, errp)) {
            return;
        }
    }
}
Exemple #2
0
/*
 * Called from the main thread on the primary
 * to setup colo-compare.
 */
static void colo_compare_complete(UserCreatable *uc, Error **errp)
{
    CompareState *s = COLO_COMPARE(uc);
    Chardev *chr;

    if (!s->pri_indev || !s->sec_indev || !s->outdev || !s->iothread) {
        error_setg(errp, "colo compare needs 'primary_in' ,"
                   "'secondary_in','outdev','iothread' property set");
        return;
    } else if (!strcmp(s->pri_indev, s->outdev) ||
               !strcmp(s->sec_indev, s->outdev) ||
               !strcmp(s->pri_indev, s->sec_indev)) {
        error_setg(errp, "'indev' and 'outdev' could not be same "
                   "for compare module");
        return;
    }

    if (find_and_check_chardev(&chr, s->pri_indev, errp) ||
        !qemu_chr_fe_init(&s->chr_pri_in, chr, errp)) {
        return;
    }

    if (find_and_check_chardev(&chr, s->sec_indev, errp) ||
        !qemu_chr_fe_init(&s->chr_sec_in, chr, errp)) {
        return;
    }

    if (find_and_check_chardev(&chr, s->outdev, errp) ||
        !qemu_chr_fe_init(&s->chr_out, chr, errp)) {
        return;
    }

    net_socket_rs_init(&s->pri_rs, compare_pri_rs_finalize, s->vnet_hdr);
    net_socket_rs_init(&s->sec_rs, compare_sec_rs_finalize, s->vnet_hdr);

    g_queue_init(&s->conn_list);

    s->connection_track_table = g_hash_table_new_full(connection_key_hash,
                                                      connection_key_equal,
                                                      g_free,
                                                      connection_destroy);

    colo_compare_iothread(s);
    return;
}
Exemple #3
0
static int con_init(struct XenDevice *xendev)
{
    struct XenConsole *con = container_of(xendev, struct XenConsole, xendev);
    char *type, *dom, label[32];
    int ret = 0;
    const char *output;

    /* setup */
    dom = xs_get_domain_path(xenstore, con->xendev.dom);
    if (!xendev->dev) {
        snprintf(con->console, sizeof(con->console), "%s/console", dom);
    } else {
        snprintf(con->console, sizeof(con->console), "%s/device/console/%d", dom, xendev->dev);
    }
    free(dom);

    type = xenstore_read_str(con->console, "type");
    if (!type || strcmp(type, "ioemu") != 0) {
        xen_pv_printf(xendev, 1, "not for me (type=%s)\n", type);
        ret = -1;
        goto out;
    }

    output = xenstore_read_str(con->console, "output");

    /* no Xen override, use qemu output device */
    if (output == NULL) {
        if (con->xendev.dev) {
            qemu_chr_fe_init(&con->chr, serial_hds[con->xendev.dev],
                             &error_abort);
        }
    } else {
        snprintf(label, sizeof(label), "xencons%d", con->xendev.dev);
        qemu_chr_fe_init(&con->chr,
                         qemu_chr_new(label, output), &error_abort);
    }

    xenstore_store_pv_console_info(con->xendev.dev,
                                   qemu_chr_fe_get_driver(&con->chr));

out:
    g_free(type);
    return ret;
}
Exemple #4
0
static void test_server_create_chr(TestServer *server, const gchar *opt)
{
    gchar *chr_path;
    Chardev *chr;

    chr_path = g_strdup_printf("unix:%s%s", server->socket_path, opt);
    chr = qemu_chr_new(server->chr_name, chr_path);
    g_free(chr_path);

    qemu_chr_fe_init(&server->chr, chr, &error_abort);
    qemu_chr_fe_set_handlers(&server->chr, chr_can_read, chr_read,
                             chr_event, NULL, server, NULL, true);
}
Exemple #5
0
/*
 * Create UART device.
 */
SiFiveUARTState *sifive_uart_create(MemoryRegion *address_space, hwaddr base,
    Chardev *chr, qemu_irq irq)
{
    SiFiveUARTState *s = g_malloc0(sizeof(SiFiveUARTState));
    s->irq = irq;
    qemu_chr_fe_init(&s->chr, chr, &error_abort);
    qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, uart_event,
        uart_be_change, s, NULL, true);
    memory_region_init_io(&s->mmio, NULL, &uart_ops, s,
                          TYPE_SIFIVE_UART, SIFIVE_UART_MAX);
    memory_region_add_subregion(address_space, base, &s->mmio);
    return s;
}
Exemple #6
0
static void filter_mirror_setup(NetFilterState *nf, Error **errp)
{
    MirrorState *s = FILTER_MIRROR(nf);
    Chardev *chr;

    chr = qemu_chr_find(s->outdev);
    if (chr == NULL) {
        error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
                  "Device '%s' not found", s->outdev);
        return;
    }

    qemu_chr_fe_init(&s->chr_out, chr, errp);
}
Exemple #7
0
static void char_ringbuf_test(void)
{
    QemuOpts *opts;
    Chardev *chr;
    CharBackend be;
    char *data;
    int ret;

    opts = qemu_opts_create(qemu_find_opts("chardev"), "ringbuf-label",
                            1, &error_abort);
    qemu_opt_set(opts, "backend", "ringbuf", &error_abort);

    qemu_opt_set(opts, "size", "5", &error_abort);
    chr = qemu_chr_new_from_opts(opts, NULL);
    g_assert_null(chr);
    qemu_opts_del(opts);

    opts = qemu_opts_create(qemu_find_opts("chardev"), "ringbuf-label",
                            1, &error_abort);
    qemu_opt_set(opts, "backend", "ringbuf", &error_abort);
    qemu_opt_set(opts, "size", "2", &error_abort);
    chr = qemu_chr_new_from_opts(opts, &error_abort);
    g_assert_nonnull(chr);
    qemu_opts_del(opts);

    qemu_chr_fe_init(&be, chr, &error_abort);
    ret = qemu_chr_fe_write(&be, (void *)"buff", 4);
    g_assert_cmpint(ret, ==, 4);

    data = qmp_ringbuf_read("ringbuf-label", 4, false, 0, &error_abort);
    g_assert_cmpstr(data, ==, "ff");
    g_free(data);

    data = qmp_ringbuf_read("ringbuf-label", 4, false, 0, &error_abort);
    g_assert_cmpstr(data, ==, "");
    g_free(data);

    qemu_chr_fe_deinit(&be, true);

    /* check alias */
    opts = qemu_opts_create(qemu_find_opts("chardev"), "memory-label",
                            1, &error_abort);
    qemu_opt_set(opts, "backend", "memory", &error_abort);
    qemu_opt_set(opts, "size", "2", &error_abort);
    chr = qemu_chr_new_from_opts(opts, NULL);
    g_assert_nonnull(chr);
    object_unparent(OBJECT(chr));
    qemu_opts_del(opts);
}
Exemple #8
0
static void char_stdio_test_subprocess(void)
{
    Chardev *chr;
    CharBackend be;
    int ret;

    chr = qemu_chr_new("label", "stdio");
    g_assert_nonnull(chr);

    qemu_chr_fe_init(&be, chr, &error_abort);
    qemu_chr_fe_set_open(&be, true);
    ret = qemu_chr_fe_write(&be, (void *)"buf", 4);
    g_assert_cmpint(ret, ==, 4);

    qemu_chr_fe_deinit(&be, true);
}
Exemple #9
0
/* If fd is zero, it means that the parallel device uses the console */
bool parallel_mm_init(MemoryRegion *address_space,
                      hwaddr base, int it_shift, qemu_irq irq,
                      Chardev *chr)
{
    ParallelState *s;

    s = g_malloc0(sizeof(ParallelState));
    s->irq = irq;
    qemu_chr_fe_init(&s->chr, chr, &error_abort);
    s->it_shift = it_shift;
    qemu_register_reset(parallel_reset, s);

    memory_region_init_io(&s->iomem, NULL, &parallel_mm_ops, s,
                          "parallel", 8 << it_shift);
    memory_region_add_subregion(address_space, base, &s->iomem);
    return true;
}
Exemple #10
0
static int net_vhost_user_init(NetClientState *peer, const char *device,
                               const char *name, Chardev *chr,
                               int queues)
{
    Error *err = NULL;
    NetClientState *nc, *nc0 = NULL;
    VhostUserState *s;
    int i;

    assert(name);
    assert(queues > 0);

    for (i = 0; i < queues; i++) {
        nc = qemu_new_net_client(&net_vhost_user_info, peer, device, name);
        snprintf(nc->info_str, sizeof(nc->info_str), "vhost-user%d to %s",
                 i, chr->label);
        nc->queue_index = i;
        if (!nc0) {
            nc0 = nc;
            s = DO_UPCAST(VhostUserState, nc, nc);
            if (!qemu_chr_fe_init(&s->chr, chr, &err)) {
                error_report_err(err);
                return -1;
            }
        }

    }

    s = DO_UPCAST(VhostUserState, nc, nc0);
    do {
        if (qemu_chr_fe_wait_connected(&s->chr, &err) < 0) {
            error_report_err(err);
            return -1;
        }
        qemu_chr_fe_set_handlers(&s->chr, NULL, NULL,
                                 net_vhost_user_event, NULL, nc0->name, NULL,
                                 true);
    } while (!s->started);

    assert(s->vhost_net);

    return 0;
}
Exemple #11
0
static void qemu_chr_open_mux(Chardev *chr,
                              ChardevBackend *backend,
                              bool *be_opened,
                              Error **errp)
{
    ChardevMux *mux = backend->u.mux.data;
    Chardev *drv;
    MuxChardev *d = MUX_CHARDEV(chr);

    drv = qemu_chr_find(mux->chardev);
    if (drv == NULL) {
        error_setg(errp, "mux: base chardev %s not found", mux->chardev);
        return;
    }

    d->focus = -1;
    /* only default to opened state if we've realized the initial
     * set of muxes
     */
    *be_opened = machine_init_done;
    qemu_chr_fe_init(&d->chr, drv, errp);
}
static void set_chr(Object *obj, Visitor *v, const char *name, void *opaque,
                    Error **errp)
{
    DeviceState *dev = DEVICE(obj);
    Error *local_err = NULL;
    Property *prop = opaque;
    CharBackend *be = qdev_get_prop_ptr(dev, prop);
    CharDriverState *s;
    char *str;

    if (dev->realized) {
        qdev_prop_set_after_realize(dev, name, errp);
        return;
    }

    visit_type_str(v, name, &str, &local_err);
    if (local_err) {
        error_propagate(errp, local_err);
        return;
    }

    if (!*str) {
        g_free(str);
        be->chr = NULL;
        return;
    }

    s = qemu_chr_find(str);
    if (s == NULL) {
        error_setg(errp, "Property '%s.%s' can't find value '%s'",
                   object_get_typename(obj), prop->name, str);
    } else if (!qemu_chr_fe_init(be, s, errp)) {
        error_prepend(errp, "Property '%s.%s' can't take value '%s': ",
                      object_get_typename(obj), prop->name, str);
    }
    g_free(str);
}
Exemple #13
0
static int tpm_emulator_handle_device_opts(TPMEmulator *tpm_emu, QemuOpts *opts)
{
    const char *value;

    value = qemu_opt_get(opts, "chardev");
    if (value) {
        Error *err = NULL;
        Chardev *dev = qemu_chr_find(value);

        if (!dev) {
            error_report("tpm-emulator: tpm chardev '%s' not found.", value);
            goto err;
        }

        if (!qemu_chr_fe_init(&tpm_emu->ctrl_chr, dev, &err)) {
            error_prepend(&err, "tpm-emulator: No valid chardev found at '%s':",
                          value);
            error_report_err(err);
            goto err;
        }

        tpm_emu->options->chardev = g_strdup(value);
    }

    if (tpm_emulator_prepare_data_fd(tpm_emu) < 0) {
        goto err;
    }

    /* FIXME: tpm_util_test_tpmdev() accepts only on socket fd, as it also used
     * by passthrough driver, which not yet using GIOChannel.
     */
    if (tpm_util_test_tpmdev(QIO_CHANNEL_SOCKET(tpm_emu->data_ioc)->fd,
                             &tpm_emu->tpm_version)) {
        error_report("'%s' is not emulating TPM device. Error: %s",
                      tpm_emu->options->chardev, strerror(errno));
        goto err;
    }

    switch (tpm_emu->tpm_version) {
    case TPM_VERSION_1_2:
        trace_tpm_emulator_handle_device_opts_tpm12();
        break;
    case TPM_VERSION_2_0:
        trace_tpm_emulator_handle_device_opts_tpm2();
        break;
    default:
        trace_tpm_emulator_handle_device_opts_unspec();
    }

    if (tpm_emulator_probe_caps(tpm_emu) ||
        tpm_emulator_check_caps(tpm_emu)) {
        goto err;
    }

    return tpm_emulator_block_migration(tpm_emu);

err:
    trace_tpm_emulator_handle_device_opts_startup_error();

    return -1;
}
Exemple #14
0
static void boston_mach_init(MachineState *machine)
{
    DeviceState *dev;
    BostonState *s;
    Error *err = NULL;
    const char *cpu_model;
    MemoryRegion *flash, *ddr, *ddr_low_alias, *lcd, *platreg;
    MemoryRegion *sys_mem = get_system_memory();
    XilinxPCIEHost *pcie2;
    PCIDevice *ahci;
    DriveInfo *hd[6];
    Chardev *chr;
    int fw_size, fit_err;
    bool is_64b;

    if ((machine->ram_size % G_BYTE) ||
        (machine->ram_size > (2 * G_BYTE))) {
        error_report("Memory size must be 1GB or 2GB");
        exit(1);
    }

    cpu_model = machine->cpu_model ?: "I6400";

    dev = qdev_create(NULL, TYPE_MIPS_BOSTON);
    qdev_init_nofail(dev);

    s = BOSTON(dev);
    s->mach = machine;
    s->cps = g_new0(MIPSCPSState, 1);

    if (!cpu_supports_cps_smp(cpu_model)) {
        error_report("Boston requires CPUs which support CPS");
        exit(1);
    }

    is_64b = cpu_supports_isa(cpu_model, ISA_MIPS64);

    object_initialize(s->cps, sizeof(MIPSCPSState), TYPE_MIPS_CPS);
    qdev_set_parent_bus(DEVICE(s->cps), sysbus_get_default());

    object_property_set_str(OBJECT(s->cps), cpu_model, "cpu-model", &err);
    object_property_set_int(OBJECT(s->cps), smp_cpus, "num-vp", &err);
    object_property_set_bool(OBJECT(s->cps), true, "realized", &err);

    if (err != NULL) {
        error_report("%s", error_get_pretty(err));
        exit(1);
    }

    sysbus_mmio_map_overlap(SYS_BUS_DEVICE(s->cps), 0, 0, 1);

    flash =  g_new(MemoryRegion, 1);
    memory_region_init_rom_device(flash, NULL, &boston_flash_ops, s,
                                  "boston.flash", 128 * M_BYTE, &err);
    memory_region_add_subregion_overlap(sys_mem, 0x18000000, flash, 0);

    ddr = g_new(MemoryRegion, 1);
    memory_region_allocate_system_memory(ddr, NULL, "boston.ddr",
                                         machine->ram_size);
    memory_region_add_subregion_overlap(sys_mem, 0x80000000, ddr, 0);

    ddr_low_alias = g_new(MemoryRegion, 1);
    memory_region_init_alias(ddr_low_alias, NULL, "boston_low.ddr",
                             ddr, 0, MIN(machine->ram_size, (256 * M_BYTE)));
    memory_region_add_subregion_overlap(sys_mem, 0, ddr_low_alias, 0);

    xilinx_pcie_init(sys_mem, 0,
                     0x10000000, 32 * M_BYTE,
                     0x40000000, 1 * G_BYTE,
                     get_cps_irq(s->cps, 2), false);

    xilinx_pcie_init(sys_mem, 1,
                     0x12000000, 32 * M_BYTE,
                     0x20000000, 512 * M_BYTE,
                     get_cps_irq(s->cps, 1), false);

    pcie2 = xilinx_pcie_init(sys_mem, 2,
                             0x14000000, 32 * M_BYTE,
                             0x16000000, 1 * M_BYTE,
                             get_cps_irq(s->cps, 0), true);

    platreg = g_new(MemoryRegion, 1);
    memory_region_init_io(platreg, NULL, &boston_platreg_ops, s,
                          "boston-platregs", 0x1000);
    memory_region_add_subregion_overlap(sys_mem, 0x17ffd000, platreg, 0);

    if (!serial_hds[0]) {
        serial_hds[0] = qemu_chr_new("serial0", "null");
    }

    s->uart = serial_mm_init(sys_mem, 0x17ffe000, 2,
                             get_cps_irq(s->cps, 3), 10000000,
                             serial_hds[0], DEVICE_NATIVE_ENDIAN);

    lcd = g_new(MemoryRegion, 1);
    memory_region_init_io(lcd, NULL, &boston_lcd_ops, s, "boston-lcd", 0x8);
    memory_region_add_subregion_overlap(sys_mem, 0x17fff000, lcd, 0);

    chr = qemu_chr_new("lcd", "vc:320x240");
    qemu_chr_fe_init(&s->lcd_display, chr, NULL);
    qemu_chr_fe_set_handlers(&s->lcd_display, NULL, NULL,
                             boston_lcd_event, s, NULL, true);

    ahci = pci_create_simple_multifunction(&PCI_BRIDGE(&pcie2->root)->sec_bus,
                                           PCI_DEVFN(0, 0),
                                           true, TYPE_ICH9_AHCI);
    g_assert(ARRAY_SIZE(hd) == ICH_AHCI(ahci)->ahci.ports);
    ide_drive_get(hd, ICH_AHCI(ahci)->ahci.ports);
    ahci_ide_create_devs(ahci, hd);

    if (machine->firmware) {
        fw_size = load_image_targphys(machine->firmware,
                                      0x1fc00000, 4 * M_BYTE);
        if (fw_size == -1) {
            error_printf("unable to load firmware image '%s'\n",
                          machine->firmware);
            exit(1);
        }
    } else if (machine->kernel_filename) {
        fit_err = load_fit(&boston_fit_loader, machine->kernel_filename, s);
        if (fit_err) {
            error_printf("unable to load FIT image\n");
            exit(1);
        }

        gen_firmware(memory_region_get_ram_ptr(flash) + 0x7c00000,
                     s->kernel_entry, s->fdt_base, is_64b);
    } else if (!qtest_enabled()) {
        error_printf("Please provide either a -kernel or -bios argument\n");
        exit(1);
    }
}
Exemple #15
0
static void char_mux_test(void)
{
    QemuOpts *opts;
    Chardev *chr, *base;
    char *data;
    FeHandler h1 = { 0, }, h2 = { 0, };
    CharBackend chr_be1, chr_be2;

    opts = qemu_opts_create(qemu_find_opts("chardev"), "mux-label",
                            1, &error_abort);
    qemu_opt_set(opts, "backend", "ringbuf", &error_abort);
    qemu_opt_set(opts, "size", "128", &error_abort);
    qemu_opt_set(opts, "mux", "on", &error_abort);
    chr = qemu_chr_new_from_opts(opts, &error_abort);
    g_assert_nonnull(chr);
    qemu_opts_del(opts);

    qemu_chr_fe_init(&chr_be1, chr, &error_abort);
    qemu_chr_fe_set_handlers(&chr_be1,
                             fe_can_read,
                             fe_read,
                             fe_event,
                             NULL,
                             &h1,
                             NULL, true);

    qemu_chr_fe_init(&chr_be2, chr, &error_abort);
    qemu_chr_fe_set_handlers(&chr_be2,
                             fe_can_read,
                             fe_read,
                             fe_event,
                             NULL,
                             &h2,
                             NULL, true);
    qemu_chr_fe_take_focus(&chr_be2);

    base = qemu_chr_find("mux-label-base");
    g_assert_cmpint(qemu_chr_be_can_write(base), !=, 0);

    qemu_chr_be_write(base, (void *)"hello", 6);
    g_assert_cmpint(h1.read_count, ==, 0);
    g_assert_cmpint(h2.read_count, ==, 6);
    g_assert_cmpstr(h2.read_buf, ==, "hello");
    h2.read_count = 0;

    /* switch focus */
    qemu_chr_be_write(base, (void *)"\1c", 2);

    qemu_chr_be_write(base, (void *)"hello", 6);
    g_assert_cmpint(h2.read_count, ==, 0);
    g_assert_cmpint(h1.read_count, ==, 6);
    g_assert_cmpstr(h1.read_buf, ==, "hello");
    h1.read_count = 0;

    /* remove first handler */
    qemu_chr_fe_set_handlers(&chr_be1, NULL, NULL, NULL, NULL,
                             NULL, NULL, true);
    qemu_chr_be_write(base, (void *)"hello", 6);
    g_assert_cmpint(h1.read_count, ==, 0);
    g_assert_cmpint(h2.read_count, ==, 0);

    qemu_chr_be_write(base, (void *)"\1c", 2);
    qemu_chr_be_write(base, (void *)"hello", 6);
    g_assert_cmpint(h1.read_count, ==, 0);
    g_assert_cmpint(h2.read_count, ==, 6);
    g_assert_cmpstr(h2.read_buf, ==, "hello");
    h2.read_count = 0;

    /* print help */
    qemu_chr_be_write(base, (void *)"\1?", 2);
    data = qmp_ringbuf_read("mux-label-base", 128, false, 0, &error_abort);
    g_assert_cmpint(strlen(data), !=, 0);
    g_free(data);

    qemu_chr_fe_deinit(&chr_be1, false);
    qemu_chr_fe_deinit(&chr_be2, true);
}
Exemple #16
0
static void char_mux_test(void)
{
    QemuOpts *opts;
    Chardev *chr, *base;
    char *data;
    FeHandler h1 = { 0, false, 0, false, }, h2 = { 0, false, 0, false, };
    CharBackend chr_be1, chr_be2;

    opts = qemu_opts_create(qemu_find_opts("chardev"), "mux-label",
                            1, &error_abort);
    qemu_opt_set(opts, "backend", "ringbuf", &error_abort);
    qemu_opt_set(opts, "size", "128", &error_abort);
    qemu_opt_set(opts, "mux", "on", &error_abort);
    chr = qemu_chr_new_from_opts(opts, NULL, &error_abort);
    g_assert_nonnull(chr);
    qemu_opts_del(opts);

    qemu_chr_fe_init(&chr_be1, chr, &error_abort);
    qemu_chr_fe_set_handlers(&chr_be1,
                             fe_can_read,
                             fe_read,
                             fe_event,
                             NULL,
                             &h1,
                             NULL, true);

    qemu_chr_fe_init(&chr_be2, chr, &error_abort);
    qemu_chr_fe_set_handlers(&chr_be2,
                             fe_can_read,
                             fe_read,
                             fe_event,
                             NULL,
                             &h2,
                             NULL, true);
    qemu_chr_fe_take_focus(&chr_be2);

    base = qemu_chr_find("mux-label-base");
    g_assert_cmpint(qemu_chr_be_can_write(base), !=, 0);

    qemu_chr_be_write(base, (void *)"hello", 6);
    g_assert_cmpint(h1.read_count, ==, 0);
    g_assert_cmpint(h2.read_count, ==, 6);
    g_assert_cmpstr(h2.read_buf, ==, "hello");
    h2.read_count = 0;

    g_assert_cmpint(h1.last_event, !=, 42); /* should be MUX_OUT or OPENED */
    g_assert_cmpint(h2.last_event, !=, 42); /* should be MUX_IN or OPENED */
    /* sending event on the base broadcast to all fe, historical reasons? */
    qemu_chr_be_event(base, 42);
    g_assert_cmpint(h1.last_event, ==, 42);
    g_assert_cmpint(h2.last_event, ==, 42);
    qemu_chr_be_event(chr, -1);
    g_assert_cmpint(h1.last_event, ==, 42);
    g_assert_cmpint(h2.last_event, ==, -1);

    /* switch focus */
    qemu_chr_be_write(base, (void *)"\1b", 2);
    g_assert_cmpint(h1.last_event, ==, 42);
    g_assert_cmpint(h2.last_event, ==, CHR_EVENT_BREAK);

    qemu_chr_be_write(base, (void *)"\1c", 2);
    g_assert_cmpint(h1.last_event, ==, CHR_EVENT_MUX_IN);
    g_assert_cmpint(h2.last_event, ==, CHR_EVENT_MUX_OUT);
    qemu_chr_be_event(chr, -1);
    g_assert_cmpint(h1.last_event, ==, -1);
    g_assert_cmpint(h2.last_event, ==, CHR_EVENT_MUX_OUT);

    qemu_chr_be_write(base, (void *)"hello", 6);
    g_assert_cmpint(h2.read_count, ==, 0);
    g_assert_cmpint(h1.read_count, ==, 6);
    g_assert_cmpstr(h1.read_buf, ==, "hello");
    h1.read_count = 0;

    qemu_chr_be_write(base, (void *)"\1b", 2);
    g_assert_cmpint(h1.last_event, ==, CHR_EVENT_BREAK);
    g_assert_cmpint(h2.last_event, ==, CHR_EVENT_MUX_OUT);

    /* open/close state and corresponding events */
    g_assert_true(qemu_chr_fe_backend_open(&chr_be1));
    g_assert_true(qemu_chr_fe_backend_open(&chr_be2));
    g_assert_true(h1.is_open);
    g_assert_false(h1.openclose_mismatch);
    g_assert_true(h2.is_open);
    g_assert_false(h2.openclose_mismatch);

    h1.openclose_count = h2.openclose_count = 0;

    qemu_chr_fe_set_handlers(&chr_be1, NULL, NULL, NULL, NULL,
                             NULL, NULL, false);
    qemu_chr_fe_set_handlers(&chr_be2, NULL, NULL, NULL, NULL,
                             NULL, NULL, false);
    g_assert_cmpint(h1.openclose_count, ==, 0);
    g_assert_cmpint(h2.openclose_count, ==, 0);

    h1.is_open = h2.is_open = false;
    qemu_chr_fe_set_handlers(&chr_be1,
                             NULL,
                             NULL,
                             fe_event,
                             NULL,
                             &h1,
                             NULL, false);
    qemu_chr_fe_set_handlers(&chr_be2,
                             NULL,
                             NULL,
                             fe_event,
                             NULL,
                             &h2,
                             NULL, false);
    g_assert_cmpint(h1.openclose_count, ==, 1);
    g_assert_false(h1.openclose_mismatch);
    g_assert_cmpint(h2.openclose_count, ==, 1);
    g_assert_false(h2.openclose_mismatch);

    qemu_chr_be_event(base, CHR_EVENT_CLOSED);
    qemu_chr_be_event(base, CHR_EVENT_OPENED);
    g_assert_cmpint(h1.openclose_count, ==, 3);
    g_assert_false(h1.openclose_mismatch);
    g_assert_cmpint(h2.openclose_count, ==, 3);
    g_assert_false(h2.openclose_mismatch);

    qemu_chr_fe_set_handlers(&chr_be2,
                             fe_can_read,
                             fe_read,
                             fe_event,
                             NULL,
                             &h2,
                             NULL, false);
    qemu_chr_fe_set_handlers(&chr_be1,
                             fe_can_read,
                             fe_read,
                             fe_event,
                             NULL,
                             &h1,
                             NULL, false);

    /* remove first handler */
    qemu_chr_fe_set_handlers(&chr_be1, NULL, NULL, NULL, NULL,
                             NULL, NULL, true);
    qemu_chr_be_write(base, (void *)"hello", 6);
    g_assert_cmpint(h1.read_count, ==, 0);
    g_assert_cmpint(h2.read_count, ==, 0);

    qemu_chr_be_write(base, (void *)"\1c", 2);
    qemu_chr_be_write(base, (void *)"hello", 6);
    g_assert_cmpint(h1.read_count, ==, 0);
    g_assert_cmpint(h2.read_count, ==, 6);
    g_assert_cmpstr(h2.read_buf, ==, "hello");
    h2.read_count = 0;

    /* print help */
    qemu_chr_be_write(base, (void *)"\1?", 2);
    data = qmp_ringbuf_read("mux-label-base", 128, false, 0, &error_abort);
    g_assert_cmpint(strlen(data), !=, 0);
    g_free(data);

    qemu_chr_fe_deinit(&chr_be1, false);
    qemu_chr_fe_deinit(&chr_be2, true);
}