All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PULL 00/41] Misc changes for 2017-01-27
@ 2017-01-27 13:45 Paolo Bonzini
  2017-01-27 13:45 ` [Qemu-devel] [PULL 01/41] icount: update instruction counter on apic patching Paolo Bonzini
                   ` (41 more replies)
  0 siblings, 42 replies; 43+ messages in thread
From: Paolo Bonzini @ 2017-01-27 13:45 UTC (permalink / raw)
  To: qemu-devel

The following changes since commit 3879284d6517dc22529395bdb259f4183b589127:

  Merge remote-tracking branch 'remotes/berrange/tags/pull-qio-2017-01-23-2' into staging (2017-01-23 15:59:09 +0000)

are available in the git repository at:


  git://github.com/bonzini/qemu.git tags/for-upstream

for you to fetch changes up to 0a24326232f7c7f8c5ab9080784312d05d496e47:

  memory: don't sign-extend 32-bit writes (2017-01-26 15:29:49 +0100)

----------------------------------------------------------------
* SCSI max_transfer support for scsi-generic (Eric)
* x86 SMI broadcast (Laszlo)
* Character device QOMification (Marc-André)
* Record/replay improvements (Pavel)
* iscsi fixes (Peter L.)
* "info mtree -f" command (Peter Xu)
* TSC clock rate reporting (Phil)
* DEVICE_CATEGORY_CPU (Thomas)
* Memory sign-extension fix (Ladi)

----------------------------------------------------------------
Eric Farman (3):
      hw/scsi: Fix debug message of cdb structure in scsi-generic
      block: Fix target variable of BLKSECTGET ioctl
      block: get max_transfer limit for char (scsi-generic) devices

Ladi Prosek (1):
      memory: don't sign-extend 32-bit writes

Laszlo Ersek (3):
      hw/isa/lpc_ich9: add SMI feature negotiation via fw_cfg
      hw/isa/lpc_ich9: add broadcast SMI feature
      hw/isa/lpc_ich9: negotiate SMI broadcast on pc-q35-2.9+ machine types

Marc-André Lureau (20):
      tests: fix linking test-char on win32
      qemu-options: stdio is available on win32
      char: add qemu_chr_fe_add_watch() Returns description
      doc: fix spelling
      char: use a const CharDriver
      char: use a static array for backends
      char: move callbacks in CharDriver
      char: fold single-user functions in caller
      char: introduce generic qemu_chr_get_kind()
      char: use a feature bit for replay
      char: allocate CharDriverState as a single object
      bt: use qemu_chr_alloc()
      char: rename CharDriverState Chardev
      char: rename TCPChardev and NetChardev
      spice-char: improve error reporting
      char: use error_report()
      gtk: overwrite the console.c char driver
      baum: use a common prefix for chr callbacks
      vc: use a common prefix for chr callbacks
      chardev: qom-ify

Pavel Dovgalyuk (7):
      icount: update instruction counter on apic patching
      replay: improve interrupt handling
      replay: don't use rtc clock on loadvm phase
      savevm: add public save_vmstate function
      replay: save/load initial state
      replay: exception replay fix
      apic: save apic_delivered flag

Peter Lieven (2):
      block/iscsi: avoid data corruption with cache=writeback
      block/iscsi: statically link qemu_iscsi_opts

Peter Xu (2):
      memory: tune mtree_print_mr() to dump mr type
      memory: hmp: add "-f" for "info mtree"

Phil Dennis-Jordan (2):
      x86-KVM: Supply TSC and APIC clock rates to guest like VMWare
      pc: Enable vmware-cpuid-freq CPU option for 2.9+ machine types

Thomas Huth (1):
      Introduce DEVICE_CATEGORY_CPU for CPU devices

 MAINTAINERS                       |    1 +
 backends/baum.c                   |  102 +-
 backends/msmouse.c                |   77 +-
 backends/rng-egd.c                |    4 +-
 backends/testdev.c                |   53 +-
 block/Makefile.objs               |    1 +
 block/file-posix.c                |   19 +-
 block/iscsi-opts.c                |   69 ++
 block/iscsi.c                     |    8 +-
 cpu-exec.c                        |    2 +-
 docs/replay.txt                   |   16 +
 exec.c                            |    2 +-
 gdbstub.c                         |   45 +-
 hmp-commands-info.hx              |    6 +-
 hw/arm/fsl-imx25.c                |    2 +-
 hw/arm/fsl-imx31.c                |    2 +-
 hw/arm/fsl-imx6.c                 |    2 +-
 hw/arm/nseries.c                  |    2 +-
 hw/arm/omap2.c                    |    2 +-
 hw/arm/pxa2xx.c                   |    2 +-
 hw/arm/virt.c                     |    2 +-
 hw/bt/hci-csr.c                   |   64 +-
 hw/char/escc.c                    |    2 +-
 hw/char/exynos4210_uart.c         |    2 +-
 hw/char/imx_serial.c              |    2 +-
 hw/char/mcf_uart.c                |    4 +-
 hw/char/omap_uart.c               |    6 +-
 hw/char/parallel.c                |    2 +-
 hw/char/serial-isa.c              |    2 +-
 hw/char/serial.c                  |    4 +-
 hw/char/sh_serial.c               |    2 +-
 hw/char/spapr_vty.c               |    2 +-
 hw/char/virtio-console.c          |    2 +-
 hw/core/qdev-properties-system.c  |    4 +-
 hw/cpu/core.c                     |    8 +
 hw/display/milkymist-tmu2.c       |    2 +-
 hw/display/sm501.c                |    2 +-
 hw/i386/kvmvapic.c                |    6 +
 hw/intc/apic_common.c             |   33 +
 hw/isa/isa-bus.c                  |    2 +-
 hw/isa/lpc_ich9.c                 |   91 +-
 hw/isa/pc87312.c                  |    2 +-
 hw/lm32/lm32.h                    |    4 +-
 hw/lm32/milkymist-hw.h            |    2 +-
 hw/mips/mips_malta.c              |    4 +-
 hw/misc/ivshmem.c                 |    2 +-
 hw/misc/milkymist-pfpu.c          |    2 +-
 hw/scsi/scsi-generic.c            |    5 +-
 hw/timer/mc146818rtc.c            |   15 +-
 hw/usb/ccid-card-passthru.c       |    2 +-
 hw/usb/dev-serial.c               |    6 +-
 hw/usb/redirect.c                 |    4 +-
 include/block/block.h             |    1 +
 include/exec/memory.h             |    2 +-
 include/hw/arm/exynos4210.h       |    2 +-
 include/hw/arm/omap.h             |    6 +-
 include/hw/bt.h                   |    4 +-
 include/hw/char/cadence_uart.h    |    2 +-
 include/hw/char/escc.h            |    2 +-
 include/hw/char/pl011.h           |    4 +-
 include/hw/char/serial.h          |    4 +-
 include/hw/char/xilinx_uartlite.h |    2 +-
 include/hw/cris/etraxfs.h         |    2 +-
 include/hw/devices.h              |    2 +-
 include/hw/i386/apic_internal.h   |    2 +
 include/hw/i386/ich9.h            |   13 +
 include/hw/i386/pc.h              |   12 +-
 include/hw/m68k/mcf.h             |    4 +-
 include/hw/ppc/spapr_vio.h        |    2 +-
 include/hw/qdev-core.h            |    1 +
 include/hw/qdev-properties.h      |    2 +-
 include/hw/sh4/sh.h               |    2 +-
 include/hw/sparc/grlib.h          |    2 +-
 include/hw/xen/xen.h              |    2 +-
 include/monitor/monitor.h         |    2 +-
 include/qemu/typedefs.h           |    2 +-
 include/sysemu/char.h             |  155 +--
 include/sysemu/replay.h           |   13 +-
 include/sysemu/sysemu.h           |    5 +-
 include/ui/console.h              |    2 +
 include/ui/gtk.h                  |    2 +-
 include/ui/qemu-spice.h           |    2 +-
 memory.c                          |   89 +-
 migration/savevm.c                |   33 +-
 monitor.c                         |   10 +-
 net/colo-compare.c                |    4 +-
 net/filter-mirror.c               |    4 +-
 net/slirp.c                       |    2 +-
 net/vhost-user.c                  |   10 +-
 qdev-monitor.c                    |    1 +
 qemu-char.c                       | 1914 ++++++++++++++++++++-----------------
 qemu-options.hx                   |   12 +-
 qmp.c                             |    2 +-
 qom/cpu.c                         |    1 +
 qtest.c                           |    2 +-
 replay/replay-char.c              |    8 +-
 replay/replay-snapshot.c          |   17 +
 replay/replay.c                   |    5 +
 spice-qemu-char.c                 |  231 +++--
 stubs/monitor.c                   |    2 +-
 stubs/replay.c                    |    4 +-
 target/i386/cpu.c                 |    1 +
 target/i386/cpu.h                 |    4 +
 target/i386/kvm.c                 |   36 +-
 target/i386/seg_helper.c          |    1 +
 tests/Makefile.include            |    2 +-
 tests/test-char.c                 |   10 +-
 tests/vhost-user-test.c           |    4 +-
 translate-all.c                   |    2 +
 ui/console.c                      |  111 ++-
 ui/gtk.c                          |   86 +-
 vl.c                              |   59 +-
 xen-common-stub.c                 |    2 +-
 xen-common.c                      |    4 +-
 114 files changed, 2192 insertions(+), 1448 deletions(-)
 create mode 100644 block/iscsi-opts.c
-- 
1.8.3.1

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [Qemu-devel] [PULL 01/41] icount: update instruction counter on apic patching
  2017-01-27 13:45 [Qemu-devel] [PULL 00/41] Misc changes for 2017-01-27 Paolo Bonzini
@ 2017-01-27 13:45 ` Paolo Bonzini
  2017-01-27 13:45 ` [Qemu-devel] [PULL 02/41] replay: improve interrupt handling Paolo Bonzini
                   ` (40 subsequent siblings)
  41 siblings, 0 replies; 43+ messages in thread
From: Paolo Bonzini @ 2017-01-27 13:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: Pavel Dovgalyuk

From: Pavel Dovgalyuk <pavel.dovgaluk@ispras.ru>

kvmvapic patches the code when some instructions are executed.
E.g. mov 0xff, 0xfffe0080 is interpreted as push 0xff/call ...
This patching is also followed by some side effects (changing apic
and guest memory state). Therefore deterministic execution should take
this operation into account. This patch decreases icount when original
mov instruction is trying to execute. Therefore patching becomes
deterministic and can be replayed correctly.

Signed-off-by: Pavel Dovgalyuk <pavel.dovgaluk@ispras.ru>
Message-Id: <20170124071702.4572.17294.stgit@PASHA-ISP>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 hw/i386/kvmvapic.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/hw/i386/kvmvapic.c b/hw/i386/kvmvapic.c
index 2f767b6..6804661 100644
--- a/hw/i386/kvmvapic.c
+++ b/hw/i386/kvmvapic.c
@@ -413,6 +413,12 @@ static void patch_instruction(VAPICROMState *s, X86CPU *cpu, target_ulong ip)
     if (!kvm_enabled()) {
         cpu_get_tb_cpu_state(env, &current_pc, &current_cs_base,
                              &current_flags);
+        /* Account this instruction, because we will exit the tb.
+           This is the first instruction in the block. Therefore
+           there is no need in restoring CPU state. */
+        if (use_icount) {
+            --cs->icount_decr.u16.low;
+        }
     }
 
     pause_all_vcpus();
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [Qemu-devel] [PULL 02/41] replay: improve interrupt handling
  2017-01-27 13:45 [Qemu-devel] [PULL 00/41] Misc changes for 2017-01-27 Paolo Bonzini
  2017-01-27 13:45 ` [Qemu-devel] [PULL 01/41] icount: update instruction counter on apic patching Paolo Bonzini
@ 2017-01-27 13:45 ` Paolo Bonzini
  2017-01-27 13:45 ` [Qemu-devel] [PULL 03/41] replay: don't use rtc clock on loadvm phase Paolo Bonzini
                   ` (39 subsequent siblings)
  41 siblings, 0 replies; 43+ messages in thread
From: Paolo Bonzini @ 2017-01-27 13:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: Pavel Dovgalyuk

From: Pavel Dovgalyuk <pavel.dovgaluk@ispras.ru>

This patch improves interrupt handling in record/replay mode.
Now "interrupt" event is saved only when cc->cpu_exec_interrupt returns true.
This patch also adds missing return to cpu_exec_interrupt function.

Signed-off-by: Pavel Dovgalyuk <pavel.dovgaluk@ispras.ru>
Message-Id: <20170124071708.4572.64023.stgit@PASHA-ISP>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 cpu-exec.c               | 2 +-
 target/i386/seg_helper.c | 1 +
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/cpu-exec.c b/cpu-exec.c
index 4188fed..fa08c73 100644
--- a/cpu-exec.c
+++ b/cpu-exec.c
@@ -508,8 +508,8 @@ static inline void cpu_handle_interrupt(CPUState *cpu,
            True when it is, and we should restart on a new TB,
            and via longjmp via cpu_loop_exit.  */
         else {
-            replay_interrupt();
             if (cc->cpu_exec_interrupt(cpu, interrupt_request)) {
+                replay_interrupt();
                 *last_tb = NULL;
             }
             /* The target hook may have updated the 'cpu->interrupt_request';
diff --git a/target/i386/seg_helper.c b/target/i386/seg_helper.c
index fb79f31..d24574d 100644
--- a/target/i386/seg_helper.c
+++ b/target/i386/seg_helper.c
@@ -1331,6 +1331,7 @@ bool x86_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
 #endif
     if (interrupt_request & CPU_INTERRUPT_SIPI) {
         do_cpu_sipi(cpu);
+        ret = true;
     } else if (env->hflags2 & HF2_GIF_MASK) {
         if ((interrupt_request & CPU_INTERRUPT_SMI) &&
             !(env->hflags & HF_SMM_MASK)) {
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [Qemu-devel] [PULL 03/41] replay: don't use rtc clock on loadvm phase
  2017-01-27 13:45 [Qemu-devel] [PULL 00/41] Misc changes for 2017-01-27 Paolo Bonzini
  2017-01-27 13:45 ` [Qemu-devel] [PULL 01/41] icount: update instruction counter on apic patching Paolo Bonzini
  2017-01-27 13:45 ` [Qemu-devel] [PULL 02/41] replay: improve interrupt handling Paolo Bonzini
@ 2017-01-27 13:45 ` Paolo Bonzini
  2017-01-27 13:45 ` [Qemu-devel] [PULL 04/41] savevm: add public save_vmstate function Paolo Bonzini
                   ` (38 subsequent siblings)
  41 siblings, 0 replies; 43+ messages in thread
From: Paolo Bonzini @ 2017-01-27 13:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: Pavel Dovgalyuk

From: Pavel Dovgalyuk <pavel.dovgaluk@ispras.ru>

This patch disables the update of the periodic timer of mc146818rtc
in record/replay mode. State of this timer is saved and therefore does
not need to be updated in record/replay mode.
Read of RTC breaks the replay because all rtc reads have to be the same
as in record mode.

Signed-off-by: Pavel Dovgalyuk <pavel.dovgaluk@ispras.ru>
Message-Id: <20170124071730.4572.41874.stgit@PASHA-ISP>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 hw/timer/mc146818rtc.c | 15 +++++++++++----
 1 file changed, 11 insertions(+), 4 deletions(-)

diff --git a/hw/timer/mc146818rtc.c b/hw/timer/mc146818rtc.c
index 637f872..4165450 100644
--- a/hw/timer/mc146818rtc.c
+++ b/hw/timer/mc146818rtc.c
@@ -27,6 +27,7 @@
 #include "hw/hw.h"
 #include "qemu/timer.h"
 #include "sysemu/sysemu.h"
+#include "sysemu/replay.h"
 #include "hw/timer/mc146818rtc.h"
 #include "qapi/visitor.h"
 #include "qapi-event.h"
@@ -734,10 +735,16 @@ static int rtc_post_load(void *opaque, int version_id)
         check_update_timer(s);
     }
 
-    uint64_t now = qemu_clock_get_ns(rtc_clock);
-    if (now < s->next_periodic_time ||
-        now > (s->next_periodic_time + get_max_clock_jump())) {
-        periodic_timer_update(s, qemu_clock_get_ns(rtc_clock));
+    /* The periodic timer is deterministic in record/replay mode,
+     * so there is no need to update it after loading the vmstate.
+     * Reading RTC here would misalign record and replay.
+     */
+    if (replay_mode == REPLAY_MODE_NONE) {
+        uint64_t now = qemu_clock_get_ns(rtc_clock);
+        if (now < s->next_periodic_time ||
+            now > (s->next_periodic_time + get_max_clock_jump())) {
+            periodic_timer_update(s, qemu_clock_get_ns(rtc_clock));
+        }
     }
 
 #ifdef TARGET_I386
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [Qemu-devel] [PULL 04/41] savevm: add public save_vmstate function
  2017-01-27 13:45 [Qemu-devel] [PULL 00/41] Misc changes for 2017-01-27 Paolo Bonzini
                   ` (2 preceding siblings ...)
  2017-01-27 13:45 ` [Qemu-devel] [PULL 03/41] replay: don't use rtc clock on loadvm phase Paolo Bonzini
@ 2017-01-27 13:45 ` Paolo Bonzini
  2017-01-27 13:45 ` [Qemu-devel] [PULL 05/41] replay: save/load initial state Paolo Bonzini
                   ` (37 subsequent siblings)
  41 siblings, 0 replies; 43+ messages in thread
From: Paolo Bonzini @ 2017-01-27 13:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: Pavel Dovgalyuk

From: Pavel Dovgalyuk <pavel.dovgaluk@ispras.ru>

This patch introduces save_vmstate function to allow saving and loading
vmstates from the replay module.

Signed-off-by: Pavel Dovgalyuk <pavel.dovgaluk@ispras.ru>
Message-Id: <20170124071741.4572.13714.stgit@PASHA-ISP>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 include/sysemu/sysemu.h |  1 +
 migration/savevm.c      | 33 ++++++++++++++++++++++-----------
 2 files changed, 23 insertions(+), 11 deletions(-)

diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h
index ff8ffb5..4ef2eb0 100644
--- a/include/sysemu/sysemu.h
+++ b/include/sysemu/sysemu.h
@@ -74,6 +74,7 @@ void qemu_add_machine_init_done_notifier(Notifier *notify);
 void qemu_remove_machine_init_done_notifier(Notifier *notify);
 
 void hmp_savevm(Monitor *mon, const QDict *qdict);
+int save_vmstate(Monitor *mon, const char *name);
 int load_vmstate(const char *name);
 void hmp_delvm(Monitor *mon, const QDict *qdict);
 void hmp_info_snapshots(Monitor *mon, const QDict *qdict);
diff --git a/migration/savevm.c b/migration/savevm.c
index f9c06e9..1e69225 100644
--- a/migration/savevm.c
+++ b/migration/savevm.c
@@ -2039,38 +2039,40 @@ int qemu_loadvm_state(QEMUFile *f)
     return ret;
 }
 
-void hmp_savevm(Monitor *mon, const QDict *qdict)
+int save_vmstate(Monitor *mon, const char *name)
 {
     BlockDriverState *bs, *bs1;
     QEMUSnapshotInfo sn1, *sn = &sn1, old_sn1, *old_sn = &old_sn1;
-    int ret;
+    int ret = -1;
     QEMUFile *f;
     int saved_vm_running;
     uint64_t vm_state_size;
     qemu_timeval tv;
     struct tm tm;
-    const char *name = qdict_get_try_str(qdict, "name");
     Error *local_err = NULL;
     AioContext *aio_context;
 
     if (!bdrv_all_can_snapshot(&bs)) {
         monitor_printf(mon, "Device '%s' is writable but does not "
                        "support snapshots.\n", bdrv_get_device_name(bs));
-        return;
+        return ret;
     }
 
     /* Delete old snapshots of the same name */
-    if (name && bdrv_all_delete_snapshot(name, &bs1, &local_err) < 0) {
-        error_reportf_err(local_err,
-                          "Error while deleting snapshot on device '%s': ",
-                          bdrv_get_device_name(bs1));
-        return;
+    if (name) {
+        ret = bdrv_all_delete_snapshot(name, &bs1, &local_err);
+        if (ret < 0) {
+            error_reportf_err(local_err,
+                              "Error while deleting snapshot on device '%s': ",
+                              bdrv_get_device_name(bs1));
+            return ret;
+        }
     }
 
     bs = bdrv_all_find_vmstate_bs();
     if (bs == NULL) {
         monitor_printf(mon, "No block device can accept snapshots\n");
-        return;
+        return ret;
     }
     aio_context = bdrv_get_aio_context(bs);
 
@@ -2079,7 +2081,7 @@ void hmp_savevm(Monitor *mon, const QDict *qdict)
     ret = global_state_store();
     if (ret) {
         monitor_printf(mon, "Error saving global state\n");
-        return;
+        return ret;
     }
     vm_stop(RUN_STATE_SAVE_VM);
 
@@ -2125,13 +2127,22 @@ void hmp_savevm(Monitor *mon, const QDict *qdict)
     if (ret < 0) {
         monitor_printf(mon, "Error while creating snapshot on '%s'\n",
                        bdrv_get_device_name(bs));
+        goto the_end;
     }
 
+    ret = 0;
+
  the_end:
     aio_context_release(aio_context);
     if (saved_vm_running) {
         vm_start();
     }
+    return ret;
+}
+
+void hmp_savevm(Monitor *mon, const QDict *qdict)
+{
+    save_vmstate(mon, qdict_get_try_str(qdict, "name"));
 }
 
 void qmp_xen_save_devices_state(const char *filename, Error **errp)
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [Qemu-devel] [PULL 05/41] replay: save/load initial state
  2017-01-27 13:45 [Qemu-devel] [PULL 00/41] Misc changes for 2017-01-27 Paolo Bonzini
                   ` (3 preceding siblings ...)
  2017-01-27 13:45 ` [Qemu-devel] [PULL 04/41] savevm: add public save_vmstate function Paolo Bonzini
@ 2017-01-27 13:45 ` Paolo Bonzini
  2017-01-27 13:45 ` [Qemu-devel] [PULL 06/41] replay: exception replay fix Paolo Bonzini
                   ` (36 subsequent siblings)
  41 siblings, 0 replies; 43+ messages in thread
From: Paolo Bonzini @ 2017-01-27 13:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: Pavel Dovgalyuk

From: Pavel Dovgalyuk <pavel.dovgaluk@ispras.ru>

This patch implements initial vmstate creation or loading at the start
of record/replay. It is needed for rewinding the execution in the replay mode.

v4 changes:
 - snapshots are not created by default anymore

v3 changes:
 - added rrsnapshot option

Signed-off-by: Pavel Dovgalyuk <pavel.dovgaluk@ispras.ru>
Message-Id: <20170124071746.4572.61449.stgit@PASHA-ISP>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 docs/replay.txt          | 16 ++++++++++++++++
 include/sysemu/replay.h  |  9 +++++++++
 qemu-options.hx          |  8 ++++++--
 replay/replay-snapshot.c | 17 +++++++++++++++++
 replay/replay.c          |  5 +++++
 vl.c                     |  7 ++++++-
 6 files changed, 59 insertions(+), 3 deletions(-)

diff --git a/docs/replay.txt b/docs/replay.txt
index 347b2ff..03e1931 100644
--- a/docs/replay.txt
+++ b/docs/replay.txt
@@ -196,6 +196,22 @@ is recorded to the log. In replay phase the queue is matched with
 events read from the log. Therefore block devices requests are processed
 deterministically.
 
+Snapshotting
+------------
+
+New VM snapshots may be created in replay mode. They can be used later
+to recover the desired VM state. All VM states created in replay mode
+are associated with the moment of time in the replay scenario.
+After recovering the VM state replay will start from that position.
+
+Default starting snapshot name may be specified with icount field
+rrsnapshot as follows:
+ -icount shift=7,rr=record,rrfile=replay.bin,rrsnapshot=snapshot_name
+
+This snapshot is created at start of recording and restored at start
+of replaying. It also can be loaded while replaying to roll back
+the execution.
+
 Network devices
 ---------------
 
diff --git a/include/sysemu/replay.h b/include/sysemu/replay.h
index abb35ca..740b425 100644
--- a/include/sysemu/replay.h
+++ b/include/sysemu/replay.h
@@ -43,6 +43,9 @@ typedef struct ReplayNetState ReplayNetState;
 
 extern ReplayMode replay_mode;
 
+/* Name of the initial VM snapshot */
+extern char *replay_snapshot;
+
 /* Replay process control functions */
 
 /*! Enables recording or saving event log with specified parameters */
@@ -149,4 +152,10 @@ void replay_unregister_net(ReplayNetState *rns);
 void replay_net_packet_event(ReplayNetState *rns, unsigned flags,
                              const struct iovec *iov, int iovcnt);
 
+/* VM state operations */
+
+/*! Called at the start of execution.
+    Loads or saves initial vmstate depending on execution mode. */
+void replay_vmstate_init(void);
+
 #endif
diff --git a/qemu-options.hx b/qemu-options.hx
index 80df526..1b4f7d6 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -3401,12 +3401,12 @@ re-inject them.
 ETEXI
 
 DEF("icount", HAS_ARG, QEMU_OPTION_icount, \
-    "-icount [shift=N|auto][,align=on|off][,sleep=on|off,rr=record|replay,rrfile=<filename>]\n" \
+    "-icount [shift=N|auto][,align=on|off][,sleep=on|off,rr=record|replay,rrfile=<filename>,rrsnapshot=<snapshot>]\n" \
     "                enable virtual instruction counter with 2^N clock ticks per\n" \
     "                instruction, enable aligning the host and virtual clocks\n" \
     "                or disable real time cpu sleeping\n", QEMU_ARCH_ALL)
 STEXI
-@item -icount [shift=@var{N}|auto][,rr=record|replay,rrfile=@var{filename}]
+@item -icount [shift=@var{N}|auto][,rr=record|replay,rrfile=@var{filename},rrsnapshot=@var{snapshot}]
 @findex -icount
 Enable virtual instruction counter.  The virtual cpu will execute one
 instruction every 2^@var{N} ns of virtual time.  If @code{auto} is specified
@@ -3439,6 +3439,10 @@ when the shift value is high (how high depends on the host machine).
 When @option{rr} option is specified deterministic record/replay is enabled.
 Replay log is written into @var{filename} file in record mode and
 read from this file in replay mode.
+
+Option rrsnapshot is used to create new vm snapshot named @var{snapshot}
+at the start of execution recording. In replay mode this option is used
+to load the initial VM state.
 ETEXI
 
 DEF("watchdog", HAS_ARG, QEMU_OPTION_watchdog, \
diff --git a/replay/replay-snapshot.c b/replay/replay-snapshot.c
index 4980597..65e2d37 100644
--- a/replay/replay-snapshot.c
+++ b/replay/replay-snapshot.c
@@ -59,3 +59,20 @@ void replay_vmstate_register(void)
 {
     vmstate_register(NULL, 0, &vmstate_replay, &replay_state);
 }
+
+void replay_vmstate_init(void)
+{
+    if (replay_snapshot) {
+        if (replay_mode == REPLAY_MODE_RECORD) {
+            if (save_vmstate(cur_mon, replay_snapshot) != 0) {
+                error_report("Could not create snapshot for icount record");
+                exit(1);
+            }
+        } else if (replay_mode == REPLAY_MODE_PLAY) {
+            if (load_vmstate(replay_snapshot) != 0) {
+                error_report("Could not load snapshot for icount replay");
+                exit(1);
+            }
+        }
+    }
+}
diff --git a/replay/replay.c b/replay/replay.c
index 7f27cf1..1835b99 100644
--- a/replay/replay.c
+++ b/replay/replay.c
@@ -26,6 +26,7 @@
 #define HEADER_SIZE                 (sizeof(uint32_t) + sizeof(uint64_t))
 
 ReplayMode replay_mode = REPLAY_MODE_NONE;
+char *replay_snapshot;
 
 /* Name of replay file  */
 static char *replay_filename;
@@ -292,6 +293,7 @@ void replay_configure(QemuOpts *opts)
         exit(1);
     }
 
+    replay_snapshot = g_strdup(qemu_opt_get(opts, "rrsnapshot"));
     replay_vmstate_register();
     replay_enable(fname, mode);
 
@@ -346,6 +348,9 @@ void replay_finish(void)
         replay_filename = NULL;
     }
 
+    g_free(replay_snapshot);
+    replay_snapshot = NULL;
+
     replay_finish_events();
     replay_mutex_destroy();
 }
diff --git a/vl.c b/vl.c
index a260f30..1f39cc0 100644
--- a/vl.c
+++ b/vl.c
@@ -464,6 +464,9 @@ static QemuOptsList qemu_icount_opts = {
         }, {
             .name = "rrfile",
             .type = QEMU_OPT_STRING,
+        }, {
+            .name = "rrsnapshot",
+            .type = QEMU_OPT_STRING,
         },
         { /* end of list */ }
     },
@@ -4619,7 +4622,9 @@ int main(int argc, char **argv, char **envp)
     replay_checkpoint(CHECKPOINT_RESET);
     qemu_system_reset(VMRESET_SILENT);
     register_global_state();
-    if (loadvm) {
+    if (replay_mode != REPLAY_MODE_NONE) {
+        replay_vmstate_init();
+    } else if (loadvm) {
         if (load_vmstate(loadvm) < 0) {
             autostart = 0;
         }
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [Qemu-devel] [PULL 06/41] replay: exception replay fix
  2017-01-27 13:45 [Qemu-devel] [PULL 00/41] Misc changes for 2017-01-27 Paolo Bonzini
                   ` (4 preceding siblings ...)
  2017-01-27 13:45 ` [Qemu-devel] [PULL 05/41] replay: save/load initial state Paolo Bonzini
@ 2017-01-27 13:45 ` Paolo Bonzini
  2017-01-27 13:45 ` [Qemu-devel] [PULL 07/41] apic: save apic_delivered flag Paolo Bonzini
                   ` (35 subsequent siblings)
  41 siblings, 0 replies; 43+ messages in thread
From: Paolo Bonzini @ 2017-01-27 13:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: Pavel Dovgalyuk

From: Pavel Dovgalyuk <pavel.dovgaluk@ispras.ru>

This patch fixes replaying the exception when TB cache is full.
It breaks cpu loop execution through setting exception_index
to process such queued work as TB flush.

v8: moved setting of exeption_index to tb_gen_code

Signed-off-by: Pavel Dovgalyuk <pavel.dovgaluk@ispras.ru>
Message-Id: <20170126123418.5412.33815.stgit@PASHA-ISP>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 translate-all.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/translate-all.c b/translate-all.c
index 2026293..6d2fcab 100644
--- a/translate-all.c
+++ b/translate-all.c
@@ -1290,6 +1290,8 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
         /* flush must be done */
         tb_flush(cpu);
         mmap_unlock();
+        /* Make the execution loop process the flush as soon as possible.  */
+        cpu->exception_index = EXCP_INTERRUPT;
         cpu_loop_exit(cpu);
     }
 
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [Qemu-devel] [PULL 07/41] apic: save apic_delivered flag
  2017-01-27 13:45 [Qemu-devel] [PULL 00/41] Misc changes for 2017-01-27 Paolo Bonzini
                   ` (5 preceding siblings ...)
  2017-01-27 13:45 ` [Qemu-devel] [PULL 06/41] replay: exception replay fix Paolo Bonzini
@ 2017-01-27 13:45 ` Paolo Bonzini
  2017-01-27 13:45 ` [Qemu-devel] [PULL 08/41] memory: tune mtree_print_mr() to dump mr type Paolo Bonzini
                   ` (34 subsequent siblings)
  41 siblings, 0 replies; 43+ messages in thread
From: Paolo Bonzini @ 2017-01-27 13:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: Pavel Dovgalyuk

From: Pavel Dovgalyuk <pavel.dovgaluk@ispras.ru>

This patch implements saving/restoring of static apic_delivered variable.

v8: saving static variable only for one of the APICs

Signed-off-by: Pavel Dovgalyuk <pavel.dovgaluk@ispras.ru>
Message-Id: <20170126123429.5412.94368.stgit@PASHA-ISP>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 hw/intc/apic_common.c           | 33 +++++++++++++++++++++++++++++++++
 include/hw/i386/apic_internal.h |  2 ++
 2 files changed, 35 insertions(+)

diff --git a/hw/intc/apic_common.c b/hw/intc/apic_common.c
index 3945dfd..17df24c 100644
--- a/hw/intc/apic_common.c
+++ b/hw/intc/apic_common.c
@@ -385,6 +385,25 @@ static bool apic_common_sipi_needed(void *opaque)
     return s->wait_for_sipi != 0;
 }
 
+static bool apic_irq_delivered_needed(void *opaque)
+{
+    APICCommonState *s = APIC_COMMON(opaque);
+    return s->cpu == X86_CPU(first_cpu) && apic_irq_delivered != 0;
+}
+
+static void apic_irq_delivered_pre_save(void *opaque)
+{
+    APICCommonState *s = APIC_COMMON(opaque);
+    s->apic_irq_delivered = apic_irq_delivered;
+}
+
+static int apic_irq_delivered_post_load(void *opaque, int version_id)
+{
+    APICCommonState *s = APIC_COMMON(opaque);
+    apic_irq_delivered = s->apic_irq_delivered;
+    return 0;
+}
+
 static const VMStateDescription vmstate_apic_common_sipi = {
     .name = "apic_sipi",
     .version_id = 1,
@@ -397,6 +416,19 @@ static const VMStateDescription vmstate_apic_common_sipi = {
     }
 };
 
+static const VMStateDescription vmstate_apic_irq_delivered = {
+    .name = "apic_irq_delivered",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .needed = apic_irq_delivered_needed,
+    .pre_save = apic_irq_delivered_pre_save,
+    .post_load = apic_irq_delivered_post_load,
+    .fields = (VMStateField[]) {
+        VMSTATE_INT32(apic_irq_delivered, APICCommonState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
 static const VMStateDescription vmstate_apic_common = {
     .name = "apic",
     .version_id = 3,
@@ -431,6 +463,7 @@ static const VMStateDescription vmstate_apic_common = {
     },
     .subsections = (const VMStateDescription*[]) {
         &vmstate_apic_common_sipi,
+        &vmstate_apic_irq_delivered,
         NULL
     }
 };
diff --git a/include/hw/i386/apic_internal.h b/include/hw/i386/apic_internal.h
index 1209eb4..20ad28c 100644
--- a/include/hw/i386/apic_internal.h
+++ b/include/hw/i386/apic_internal.h
@@ -189,6 +189,8 @@ struct APICCommonState {
     DeviceState *vapic;
     hwaddr vapic_paddr; /* note: persistence via kvmvapic */
     bool legacy_instance_id;
+
+    int apic_irq_delivered; /* for saving static variable */
 };
 
 typedef struct VAPICState {
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [Qemu-devel] [PULL 08/41] memory: tune mtree_print_mr() to dump mr type
  2017-01-27 13:45 [Qemu-devel] [PULL 00/41] Misc changes for 2017-01-27 Paolo Bonzini
                   ` (6 preceding siblings ...)
  2017-01-27 13:45 ` [Qemu-devel] [PULL 07/41] apic: save apic_delivered flag Paolo Bonzini
@ 2017-01-27 13:45 ` Paolo Bonzini
  2017-01-27 13:45 ` [Qemu-devel] [PULL 09/41] memory: hmp: add "-f" for "info mtree" Paolo Bonzini
                   ` (33 subsequent siblings)
  41 siblings, 0 replies; 43+ messages in thread
From: Paolo Bonzini @ 2017-01-27 13:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: Peter Xu

From: Peter Xu <peterx@redhat.com>

We were dumping RW bits for each memory region, that might be confusing.
It'll make more sense to dump the memory region type directly rather
than the RW bits since that's how the bits are derived.

Meanwhile, with some slight cleanup in the function.

Signed-off-by: Peter Xu <peterx@redhat.com>
Message-Id: <1484556005-29701-2-git-send-email-peterx@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 memory.c | 48 +++++++++++++++++++++++++++---------------------
 1 file changed, 27 insertions(+), 21 deletions(-)

diff --git a/memory.c b/memory.c
index 2bfc37f..c42bde4 100644
--- a/memory.c
+++ b/memory.c
@@ -2450,6 +2450,21 @@ void address_space_destroy(AddressSpace *as)
     call_rcu(as, do_address_space_destroy, rcu);
 }
 
+static const char *memory_region_type(MemoryRegion *mr)
+{
+    if (memory_region_is_ram_device(mr)) {
+        return "ramd";
+    } else if (memory_region_is_romd(mr)) {
+        return "romd";
+    } else if (memory_region_is_rom(mr)) {
+        return "rom";
+    } else if (memory_region_is_ram(mr)) {
+        return "ram";
+    } else {
+        return "i/o";
+    }
+}
+
 typedef struct MemoryRegionList MemoryRegionList;
 
 struct MemoryRegionList {
@@ -2459,6 +2474,10 @@ struct MemoryRegionList {
 
 typedef QTAILQ_HEAD(queue, MemoryRegionList) MemoryRegionListHead;
 
+#define MR_SIZE(size) (int128_nz(size) ? (hwaddr)int128_get64( \
+                           int128_sub((size), int128_one())) : 0)
+#define MTREE_INDENT "  "
+
 static void mtree_print_mr(fprintf_function mon_printf, void *f,
                            const MemoryRegion *mr, unsigned int level,
                            hwaddr base,
@@ -2474,7 +2493,7 @@ static void mtree_print_mr(fprintf_function mon_printf, void *f,
     }
 
     for (i = 0; i < level; i++) {
-        mon_printf(f, "  ");
+        mon_printf(f, MTREE_INDENT);
     }
 
     if (mr->alias) {
@@ -2494,37 +2513,24 @@ static void mtree_print_mr(fprintf_function mon_printf, void *f,
             QTAILQ_INSERT_TAIL(alias_print_queue, ml, queue);
         }
         mon_printf(f, TARGET_FMT_plx "-" TARGET_FMT_plx
-                   " (prio %d, %c%c): alias %s @%s " TARGET_FMT_plx
+                   " (prio %d, %s): alias %s @%s " TARGET_FMT_plx
                    "-" TARGET_FMT_plx "%s\n",
                    base + mr->addr,
-                   base + mr->addr
-                   + (int128_nz(mr->size) ?
-                      (hwaddr)int128_get64(int128_sub(mr->size,
-                                                      int128_one())) : 0),
+                   base + mr->addr + MR_SIZE(mr->size),
                    mr->priority,
-                   mr->romd_mode ? 'R' : '-',
-                   !mr->readonly && !(mr->rom_device && mr->romd_mode) ? 'W'
-                                                                       : '-',
+                   memory_region_type((MemoryRegion *)mr),
                    memory_region_name(mr),
                    memory_region_name(mr->alias),
                    mr->alias_offset,
-                   mr->alias_offset
-                   + (int128_nz(mr->size) ?
-                      (hwaddr)int128_get64(int128_sub(mr->size,
-                                                      int128_one())) : 0),
+                   mr->alias_offset + MR_SIZE(mr->size),
                    mr->enabled ? "" : " [disabled]");
     } else {
         mon_printf(f,
-                   TARGET_FMT_plx "-" TARGET_FMT_plx " (prio %d, %c%c): %s%s\n",
+                   TARGET_FMT_plx "-" TARGET_FMT_plx " (prio %d, %s): %s%s\n",
                    base + mr->addr,
-                   base + mr->addr
-                   + (int128_nz(mr->size) ?
-                      (hwaddr)int128_get64(int128_sub(mr->size,
-                                                      int128_one())) : 0),
+                   base + mr->addr + MR_SIZE(mr->size),
                    mr->priority,
-                   mr->romd_mode ? 'R' : '-',
-                   !mr->readonly && !(mr->rom_device && mr->romd_mode) ? 'W'
-                                                                       : '-',
+                   memory_region_type((MemoryRegion *)mr),
                    memory_region_name(mr),
                    mr->enabled ? "" : " [disabled]");
     }
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [Qemu-devel] [PULL 09/41] memory: hmp: add "-f" for "info mtree"
  2017-01-27 13:45 [Qemu-devel] [PULL 00/41] Misc changes for 2017-01-27 Paolo Bonzini
                   ` (7 preceding siblings ...)
  2017-01-27 13:45 ` [Qemu-devel] [PULL 08/41] memory: tune mtree_print_mr() to dump mr type Paolo Bonzini
@ 2017-01-27 13:45 ` Paolo Bonzini
  2017-01-27 13:45 ` [Qemu-devel] [PULL 10/41] hw/isa/lpc_ich9: add SMI feature negotiation via fw_cfg Paolo Bonzini
                   ` (32 subsequent siblings)
  41 siblings, 0 replies; 43+ messages in thread
From: Paolo Bonzini @ 2017-01-27 13:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: Peter Xu

From: Peter Xu <peterx@redhat.com>

Adding one more option "-f" for "info mtree" to dump the flat views of
all the address spaces.

This will be useful to debug the memory rendering logic, also it'll be
much easier with it to know what memory region is handling what address
range.

Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
Signed-off-by: Peter Xu <peterx@redhat.com>
Message-Id: <1484556005-29701-3-git-send-email-peterx@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 hmp-commands-info.hx  |  6 +++---
 include/exec/memory.h |  2 +-
 memory.c              | 41 ++++++++++++++++++++++++++++++++++++++++-
 monitor.c             |  4 +++-
 4 files changed, 47 insertions(+), 6 deletions(-)

diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx
index 55d50c4..b0f35e6 100644
--- a/hmp-commands-info.hx
+++ b/hmp-commands-info.hx
@@ -249,9 +249,9 @@ ETEXI
 
     {
         .name       = "mtree",
-        .args_type  = "",
-        .params     = "",
-        .help       = "show memory tree",
+        .args_type  = "flatview:-f",
+        .params     = "[-f]",
+        .help       = "show memory tree (-f: dump flat view for address spaces)",
         .cmd        = hmp_info_mtree,
     },
 
diff --git a/include/exec/memory.h b/include/exec/memory.h
index a10044f..987f925 100644
--- a/include/exec/memory.h
+++ b/include/exec/memory.h
@@ -1250,7 +1250,7 @@ void memory_global_dirty_log_start(void);
  */
 void memory_global_dirty_log_stop(void);
 
-void mtree_info(fprintf_function mon_printf, void *f);
+void mtree_info(fprintf_function mon_printf, void *f, bool flatview);
 
 /**
  * memory_region_dispatch_read: perform a read directly to the specified
diff --git a/memory.c b/memory.c
index c42bde4..6498727 100644
--- a/memory.c
+++ b/memory.c
@@ -2564,12 +2564,51 @@ static void mtree_print_mr(fprintf_function mon_printf, void *f,
     }
 }
 
-void mtree_info(fprintf_function mon_printf, void *f)
+static void mtree_print_flatview(fprintf_function p, void *f,
+                                 AddressSpace *as)
+{
+    FlatView *view = address_space_get_flatview(as);
+    FlatRange *range = &view->ranges[0];
+    MemoryRegion *mr;
+    int n = view->nr;
+
+    if (n <= 0) {
+        p(f, MTREE_INDENT "No rendered FlatView for "
+          "address space '%s'\n", as->name);
+        flatview_unref(view);
+        return;
+    }
+
+    while (n--) {
+        mr = range->mr;
+        p(f, MTREE_INDENT TARGET_FMT_plx "-"
+          TARGET_FMT_plx " (prio %d, %s): %s\n",
+          int128_get64(range->addr.start),
+          int128_get64(range->addr.start) + MR_SIZE(range->addr.size),
+          mr->priority,
+          memory_region_type(mr),
+          memory_region_name(mr));
+        range++;
+    }
+
+    flatview_unref(view);
+}
+
+void mtree_info(fprintf_function mon_printf, void *f, bool flatview)
 {
     MemoryRegionListHead ml_head;
     MemoryRegionList *ml, *ml2;
     AddressSpace *as;
 
+    if (flatview) {
+        QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) {
+            mon_printf(f, "address-space (flat view): %s\n", as->name);
+            mtree_print_flatview(mon_printf, f, as);
+            mon_printf(f, "\n");
+        }
+        return;
+    }
+
     QTAILQ_INIT(&ml_head);
 
     QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) {
diff --git a/monitor.c b/monitor.c
index 8b06b63..6ac4e95 100644
--- a/monitor.c
+++ b/monitor.c
@@ -1529,7 +1529,9 @@ static void hmp_boot_set(Monitor *mon, const QDict *qdict)
 
 static void hmp_info_mtree(Monitor *mon, const QDict *qdict)
 {
-    mtree_info((fprintf_function)monitor_printf, mon);
+    bool flatview = qdict_get_try_bool(qdict, "flatview", false);
+
+    mtree_info((fprintf_function)monitor_printf, mon, flatview);
 }
 
 static void hmp_info_numa(Monitor *mon, const QDict *qdict)
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [Qemu-devel] [PULL 10/41] hw/isa/lpc_ich9: add SMI feature negotiation via fw_cfg
  2017-01-27 13:45 [Qemu-devel] [PULL 00/41] Misc changes for 2017-01-27 Paolo Bonzini
                   ` (8 preceding siblings ...)
  2017-01-27 13:45 ` [Qemu-devel] [PULL 09/41] memory: hmp: add "-f" for "info mtree" Paolo Bonzini
@ 2017-01-27 13:45 ` Paolo Bonzini
  2017-01-27 13:45 ` [Qemu-devel] [PULL 11/41] hw/isa/lpc_ich9: add broadcast SMI feature Paolo Bonzini
                   ` (31 subsequent siblings)
  41 siblings, 0 replies; 43+ messages in thread
From: Paolo Bonzini @ 2017-01-27 13:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: Laszlo Ersek, Michael S. Tsirkin, Gerd Hoffmann, Igor Mammedov

From: Laszlo Ersek <lersek@redhat.com>

Introduce the following fw_cfg files:

- "etc/smi/supported-features": a little endian uint64_t feature bitmap,
  presenting the features known by the host to the guest. Read-only for
  the guest.

  The content of this file will be determined via bit-granularity ICH9-LPC
  device properties, to be introduced later. For now, the bitmask is left
  zeroed. The bits will be set from machine type compat properties and on
  the QEMU command line, hence this file is not migrated.

- "etc/smi/requested-features": a little endian uint64_t feature bitmap,
  representing the features the guest would like to request. Read-write
  for the guest.

  The guest can freely (re)write this file, it has no direct consequence.
  Initial value is zero. A nonzero value causes the SMI-related fw_cfg
  files and fields that are under guest influence to be migrated.

- "etc/smi/features-ok": contains a uint8_t value, and it is read-only for
  the guest. When the guest selects the associated fw_cfg key, the guest
  features are validated against the host features. In case of error, the
  negotiation doesn't proceed, and the "features-ok" file remains zero. In
  case of success, the "features-ok" file becomes (uint8_t)1, and the
  negotiated features are locked down internally (to which no further
  changes are possible until reset).

  The initial value is zero.  A nonzero value causes the SMI-related
  fw_cfg files and fields that are under guest influence to be migrated.

The C-language fields backing the "supported-features" and
"requested-features" files are uint8_t arrays. This is because they carry
guest-side representation (our choice is little endian), while
VMSTATE_UINT64() assumes / implies host-side endianness for any uint64_t
fields. If we migrate a guest between hosts with different endiannesses
(which is possible with TCG), then the host-side value is preserved, and
the host-side representation is translated. This would be visible to the
guest through fw_cfg, unless we used plain byte arrays. So we do.

Cc: "Michael S. Tsirkin" <mst@redhat.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Cc: Igor Mammedov <imammedo@redhat.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Reviewed-by: Igor Mammedov <imammedo@redhat.com>
Message-Id: <20170126014416.11211-2-lersek@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 hw/isa/lpc_ich9.c      | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++
 include/hw/i386/ich9.h | 10 +++++++
 2 files changed, 89 insertions(+)

diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c
index 10d1ee8..376b780 100644
--- a/hw/isa/lpc_ich9.c
+++ b/hw/isa/lpc_ich9.c
@@ -48,6 +48,8 @@
 #include "exec/address-spaces.h"
 #include "sysemu/sysemu.h"
 #include "qom/cpu.h"
+#include "hw/nvram/fw_cfg.h"
+#include "qemu/cutils.h"
 
 /*****************************************************************************/
 /* ICH9 LPC PCI to ISA bridge */
@@ -360,13 +362,62 @@ static void ich9_set_sci(void *opaque, int irq_num, int level)
     }
 }
 
+static void smi_features_ok_callback(void *opaque)
+{
+    ICH9LPCState *lpc = opaque;
+    uint64_t guest_features;
+
+    if (lpc->smi_features_ok) {
+        /* negotiation already complete, features locked */
+        return;
+    }
+
+    memcpy(&guest_features, lpc->smi_guest_features_le, sizeof guest_features);
+    le64_to_cpus(&guest_features);
+    if (guest_features & ~lpc->smi_host_features) {
+        /* guest requests invalid features, leave @features_ok at zero */
+        return;
+    }
+
+    /* valid feature subset requested, lock it down, report success */
+    lpc->smi_negotiated_features = guest_features;
+    lpc->smi_features_ok = 1;
+}
+
 void ich9_lpc_pm_init(PCIDevice *lpc_pci, bool smm_enabled)
 {
     ICH9LPCState *lpc = ICH9_LPC_DEVICE(lpc_pci);
     qemu_irq sci_irq;
+    FWCfgState *fw_cfg = fw_cfg_find();
 
     sci_irq = qemu_allocate_irq(ich9_set_sci, lpc, 0);
     ich9_pm_init(lpc_pci, &lpc->pm, smm_enabled, sci_irq);
+
+    if (lpc->smi_host_features && fw_cfg) {
+        uint64_t host_features_le;
+
+        host_features_le = cpu_to_le64(lpc->smi_host_features);
+        memcpy(lpc->smi_host_features_le, &host_features_le,
+               sizeof host_features_le);
+        fw_cfg_add_file(fw_cfg, "etc/smi/supported-features",
+                        lpc->smi_host_features_le,
+                        sizeof lpc->smi_host_features_le);
+
+        /* The other two guest-visible fields are cleared on device reset, we
+         * just link them into fw_cfg here.
+         */
+        fw_cfg_add_file_callback(fw_cfg, "etc/smi/requested-features",
+                                 NULL, NULL,
+                                 lpc->smi_guest_features_le,
+                                 sizeof lpc->smi_guest_features_le,
+                                 false);
+        fw_cfg_add_file_callback(fw_cfg, "etc/smi/features-ok",
+                                 smi_features_ok_callback, lpc,
+                                 &lpc->smi_features_ok,
+                                 sizeof lpc->smi_features_ok,
+                                 true);
+    }
+
     ich9_lpc_reset(&lpc->d.qdev);
 }
 
@@ -507,6 +558,10 @@ static void ich9_lpc_reset(DeviceState *qdev)
 
     lpc->sci_level = 0;
     lpc->rst_cnt = 0;
+
+    memset(lpc->smi_guest_features_le, 0, sizeof lpc->smi_guest_features_le);
+    lpc->smi_features_ok = 0;
+    lpc->smi_negotiated_features = 0;
 }
 
 /* root complex register block is mapped into memory space */
@@ -668,6 +723,29 @@ static const VMStateDescription vmstate_ich9_rst_cnt = {
     }
 };
 
+static bool ich9_smi_feat_needed(void *opaque)
+{
+    ICH9LPCState *lpc = opaque;
+
+    return !buffer_is_zero(lpc->smi_guest_features_le,
+                           sizeof lpc->smi_guest_features_le) ||
+           lpc->smi_features_ok;
+}
+
+static const VMStateDescription vmstate_ich9_smi_feat = {
+    .name = "ICH9LPC/smi_feat",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .needed = ich9_smi_feat_needed,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT8_ARRAY(smi_guest_features_le, ICH9LPCState,
+                            sizeof(uint64_t)),
+        VMSTATE_UINT8(smi_features_ok, ICH9LPCState),
+        VMSTATE_UINT64(smi_negotiated_features, ICH9LPCState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
 static const VMStateDescription vmstate_ich9_lpc = {
     .name = "ICH9LPC",
     .version_id = 1,
@@ -683,6 +761,7 @@ static const VMStateDescription vmstate_ich9_lpc = {
     },
     .subsections = (const VMStateDescription*[]) {
         &vmstate_ich9_rst_cnt,
+        &vmstate_ich9_smi_feat,
         NULL
     }
 };
diff --git a/include/hw/i386/ich9.h b/include/hw/i386/ich9.h
index 5fd7e97..da11187 100644
--- a/include/hw/i386/ich9.h
+++ b/include/hw/i386/ich9.h
@@ -64,6 +64,16 @@ typedef struct ICH9LPCState {
     uint8_t rst_cnt;
     MemoryRegion rst_cnt_mem;
 
+    /* SMI feature negotiation via fw_cfg */
+    uint64_t smi_host_features;       /* guest-invisible, host endian */
+    uint8_t smi_host_features_le[8];  /* guest-visible, read-only, little
+                                       * endian uint64_t */
+    uint8_t smi_guest_features_le[8]; /* guest-visible, read-write, little
+                                       * endian uint64_t */
+    uint8_t smi_features_ok;          /* guest-visible, read-only; selecting it
+                                       * triggers feature lockdown */
+    uint64_t smi_negotiated_features; /* guest-invisible, host endian */
+
     /* isa bus */
     ISABus *isa_bus;
     MemoryRegion rcrb_mem; /* root complex register block */
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [Qemu-devel] [PULL 11/41] hw/isa/lpc_ich9: add broadcast SMI feature
  2017-01-27 13:45 [Qemu-devel] [PULL 00/41] Misc changes for 2017-01-27 Paolo Bonzini
                   ` (9 preceding siblings ...)
  2017-01-27 13:45 ` [Qemu-devel] [PULL 10/41] hw/isa/lpc_ich9: add SMI feature negotiation via fw_cfg Paolo Bonzini
@ 2017-01-27 13:45 ` Paolo Bonzini
  2017-01-27 13:45 ` [Qemu-devel] [PULL 12/41] hw/isa/lpc_ich9: negotiate SMI broadcast on pc-q35-2.9+ machine types Paolo Bonzini
                   ` (30 subsequent siblings)
  41 siblings, 0 replies; 43+ messages in thread
From: Paolo Bonzini @ 2017-01-27 13:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: Laszlo Ersek, Michael S. Tsirkin, Gerd Hoffmann, Igor Mammedov

From: Laszlo Ersek <lersek@redhat.com>

The generic edk2 SMM infrastructure prefers
EFI_SMM_CONTROL2_PROTOCOL.Trigger() to inject an SMI on each processor. If
Trigger() only brings the current processor into SMM, then edk2 handles it
in the following ways:

(1) If Trigger() is executed by the BSP (which is guaranteed before
    ExitBootServices(), but is not necessarily true at runtime), then:

    (a) If edk2 has been configured for "traditional" SMM synchronization,
        then the BSP sends directed SMIs to the APs with APIC delivery,
        bringing them into SMM individually. Then the BSP runs the SMI
        handler / dispatcher.

    (b) If edk2 has been configured for "relaxed" SMM synchronization,
        then the APs that are not already in SMM are not brought in, and
        the BSP runs the SMI handler / dispatcher.

(2) If Trigger() is executed by an AP (which is possible after
    ExitBootServices(), and can be forced e.g. by "taskset -c 1
    efibootmgr"), then the AP in question brings in the BSP with a
    directed SMI, and the BSP runs the SMI handler / dispatcher.

The smaller problem with (1a) and (2) is that the BSP and AP
synchronization is slow. For example, the "taskset -c 1 efibootmgr"
command from (2) can take more than 3 seconds to complete, because
efibootmgr accesses non-volatile UEFI variables intensively.

The larger problem is that QEMU's current behavior diverges from the
behavior usually seen on physical hardware, and that keeps exposing
obscure corner cases, race conditions and other instabilities in edk2,
which generally expects / prefers a software SMI to affect all CPUs at
once.

Therefore introduce the "broadcast SMI" feature that causes QEMU to inject
the SMI on all VCPUs.

While the original posting of this patch
<http://lists.nongnu.org/archive/html/qemu-devel/2015-10/msg05658.html>
only intended to speed up (2), based on our recent "stress testing" of SMM
this patch actually provides functional improvements.

Cc: "Michael S. Tsirkin" <mst@redhat.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Cc: Igor Mammedov <imammedo@redhat.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Reviewed-by: Igor Mammedov <imammedo@redhat.com>
Message-Id: <20170126014416.11211-3-lersek@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 hw/isa/lpc_ich9.c      | 10 +++++++++-
 include/hw/i386/ich9.h |  3 +++
 2 files changed, 12 insertions(+), 1 deletion(-)

diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c
index 376b780..ced6f80 100644
--- a/hw/isa/lpc_ich9.c
+++ b/hw/isa/lpc_ich9.c
@@ -437,7 +437,15 @@ static void ich9_apm_ctrl_changed(uint32_t val, void *arg)
 
     /* SMI_EN = PMBASE + 30. SMI control and enable register */
     if (lpc->pm.smi_en & ICH9_PMIO_SMI_EN_APMC_EN) {
-        cpu_interrupt(current_cpu, CPU_INTERRUPT_SMI);
+        if (lpc->smi_negotiated_features &
+            (UINT64_C(1) << ICH9_LPC_SMI_F_BROADCAST_BIT)) {
+            CPUState *cs;
+            CPU_FOREACH(cs) {
+                cpu_interrupt(cs, CPU_INTERRUPT_SMI);
+            }
+        } else {
+            cpu_interrupt(current_cpu, CPU_INTERRUPT_SMI);
+        }
     }
 }
 
diff --git a/include/hw/i386/ich9.h b/include/hw/i386/ich9.h
index da11187..18dcca7 100644
--- a/include/hw/i386/ich9.h
+++ b/include/hw/i386/ich9.h
@@ -250,4 +250,7 @@ Object *ich9_lpc_find(void);
 #define ICH9_SMB_HST_D1                         0x06
 #define ICH9_SMB_HOST_BLOCK_DB                  0x07
 
+/* bit positions used in fw_cfg SMI feature negotiation */
+#define ICH9_LPC_SMI_F_BROADCAST_BIT            0
+
 #endif /* HW_ICH9_H */
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [Qemu-devel] [PULL 12/41] hw/isa/lpc_ich9: negotiate SMI broadcast on pc-q35-2.9+ machine types
  2017-01-27 13:45 [Qemu-devel] [PULL 00/41] Misc changes for 2017-01-27 Paolo Bonzini
                   ` (10 preceding siblings ...)
  2017-01-27 13:45 ` [Qemu-devel] [PULL 11/41] hw/isa/lpc_ich9: add broadcast SMI feature Paolo Bonzini
@ 2017-01-27 13:45 ` Paolo Bonzini
  2017-01-27 13:45 ` [Qemu-devel] [PULL 13/41] block/iscsi: avoid data corruption with cache=writeback Paolo Bonzini
                   ` (29 subsequent siblings)
  41 siblings, 0 replies; 43+ messages in thread
From: Paolo Bonzini @ 2017-01-27 13:45 UTC (permalink / raw)
  To: qemu-devel
  Cc: Laszlo Ersek, Michael S. Tsirkin, Eduardo Habkost, Gerd Hoffmann,
	Igor Mammedov

From: Laszlo Ersek <lersek@redhat.com>

Cc: "Michael S. Tsirkin" <mst@redhat.com>
Cc: Eduardo Habkost <ehabkost@redhat.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Cc: Igor Mammedov <imammedo@redhat.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: Eduardo Habkost <ehabkost@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Reviewed-by: Igor Mammedov <imammedo@redhat.com>
Message-Id: <20170126014416.11211-4-lersek@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 hw/isa/lpc_ich9.c    | 2 ++
 include/hw/i386/pc.h | 6 ++++++
 2 files changed, 8 insertions(+)

diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c
index ced6f80..59930dd 100644
--- a/hw/isa/lpc_ich9.c
+++ b/hw/isa/lpc_ich9.c
@@ -776,6 +776,8 @@ static const VMStateDescription vmstate_ich9_lpc = {
 
 static Property ich9_lpc_properties[] = {
     DEFINE_PROP_BOOL("noreboot", ICH9LPCState, pin_strap.spkr_hi, true),
+    DEFINE_PROP_BIT64("x-smi-broadcast", ICH9LPCState, smi_host_features,
+                      ICH9_LPC_SMI_F_BROADCAST_BIT, true),
     DEFINE_PROP_END_OF_LIST(),
 };
 
diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
index 738bfd6..020b632 100644
--- a/include/hw/i386/pc.h
+++ b/include/hw/i386/pc.h
@@ -381,6 +381,12 @@ bool e820_get_entry(int, uint32_t, uint64_t *, uint64_t *);
         .property = "x-mach-use-reliable-get-clock",\
         .value    = "off",\
     },\
+    {\
+        .driver   = "ICH9-LPC",\
+        .property = "x-smi-broadcast",\
+        .value    = "off",\
+    },\
+
 
 #define PC_COMPAT_2_7 \
     HW_COMPAT_2_7 \
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [Qemu-devel] [PULL 13/41] block/iscsi: avoid data corruption with cache=writeback
  2017-01-27 13:45 [Qemu-devel] [PULL 00/41] Misc changes for 2017-01-27 Paolo Bonzini
                   ` (11 preceding siblings ...)
  2017-01-27 13:45 ` [Qemu-devel] [PULL 12/41] hw/isa/lpc_ich9: negotiate SMI broadcast on pc-q35-2.9+ machine types Paolo Bonzini
@ 2017-01-27 13:45 ` Paolo Bonzini
  2017-01-27 13:45 ` [Qemu-devel] [PULL 14/41] Introduce DEVICE_CATEGORY_CPU for CPU devices Paolo Bonzini
                   ` (28 subsequent siblings)
  41 siblings, 0 replies; 43+ messages in thread
From: Paolo Bonzini @ 2017-01-27 13:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: Peter Lieven, qemu-stable

From: Peter Lieven <pl@kamp.de>

nb_cls_shrunk in iscsi_allocmap_update can become -1 if the
request starts and ends within the same cluster. This results
in passing -1 to bitmap_set and bitmap_clear and they don't
handle negative values properly. In the end this leads to data
corruption.

Fixes: e1123a3b40a1a9a625a29c8ed4debb7e206ea690
Cc: qemu-stable@nongnu.org
Signed-off-by: Peter Lieven <pl@kamp.de>
Message-Id: <1484579832-18589-1-git-send-email-pl@kamp.de>
Reviewed-by: Fam Zheng <famz@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 block/iscsi.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/block/iscsi.c b/block/iscsi.c
index 6aeeb9e..1860f1b 100644
--- a/block/iscsi.c
+++ b/block/iscsi.c
@@ -499,14 +499,18 @@ iscsi_allocmap_update(IscsiLun *iscsilun, int64_t sector_num,
     if (allocated) {
         bitmap_set(iscsilun->allocmap, cl_num_expanded, nb_cls_expanded);
     } else {
-        bitmap_clear(iscsilun->allocmap, cl_num_shrunk, nb_cls_shrunk);
+        if (nb_cls_shrunk > 0) {
+            bitmap_clear(iscsilun->allocmap, cl_num_shrunk, nb_cls_shrunk);
+        }
     }
 
     if (iscsilun->allocmap_valid == NULL) {
         return;
     }
     if (valid) {
-        bitmap_set(iscsilun->allocmap_valid, cl_num_shrunk, nb_cls_shrunk);
+        if (nb_cls_shrunk > 0) {
+            bitmap_set(iscsilun->allocmap_valid, cl_num_shrunk, nb_cls_shrunk);
+        }
     } else {
         bitmap_clear(iscsilun->allocmap_valid, cl_num_expanded,
                      nb_cls_expanded);
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [Qemu-devel] [PULL 14/41] Introduce DEVICE_CATEGORY_CPU for CPU devices
  2017-01-27 13:45 [Qemu-devel] [PULL 00/41] Misc changes for 2017-01-27 Paolo Bonzini
                   ` (12 preceding siblings ...)
  2017-01-27 13:45 ` [Qemu-devel] [PULL 13/41] block/iscsi: avoid data corruption with cache=writeback Paolo Bonzini
@ 2017-01-27 13:45 ` Paolo Bonzini
  2017-01-27 13:45 ` [Qemu-devel] [PULL 15/41] hw/scsi: Fix debug message of cdb structure in scsi-generic Paolo Bonzini
                   ` (27 subsequent siblings)
  41 siblings, 0 replies; 43+ messages in thread
From: Paolo Bonzini @ 2017-01-27 13:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: Thomas Huth

From: Thomas Huth <thuth@redhat.com>

Now that CPUs show up in the help text of "-device ?",
we should group them into an appropriate category.

Signed-off-by: Thomas Huth <thuth@redhat.com>
Reviewed-by: Eduardo Habkost <ehabkost@redhat.com>
Message-Id: <1484917276-7107-1-git-send-email-thuth@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 hw/cpu/core.c          | 8 ++++++++
 include/hw/qdev-core.h | 1 +
 qdev-monitor.c         | 1 +
 qom/cpu.c              | 1 +
 4 files changed, 11 insertions(+)

diff --git a/hw/cpu/core.c b/hw/cpu/core.c
index eff90c1..2bf960d 100644
--- a/hw/cpu/core.c
+++ b/hw/cpu/core.c
@@ -72,10 +72,18 @@ static void cpu_core_instance_init(Object *obj)
     core->nr_threads = smp_threads;
 }
 
+static void cpu_core_class_init(ObjectClass *oc, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
+
+    set_bit(DEVICE_CATEGORY_CPU, dc->categories);
+}
+
 static const TypeInfo cpu_core_type_info = {
     .name = TYPE_CPU_CORE,
     .parent = TYPE_DEVICE,
     .abstract = true,
+    .class_init = cpu_core_class_init,
     .instance_size = sizeof(CPUCore),
     .instance_init = cpu_core_instance_init,
 };
diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h
index 2c97347..b44b476 100644
--- a/include/hw/qdev-core.h
+++ b/include/hw/qdev-core.h
@@ -26,6 +26,7 @@ typedef enum DeviceCategory {
     DEVICE_CATEGORY_DISPLAY,
     DEVICE_CATEGORY_SOUND,
     DEVICE_CATEGORY_MISC,
+    DEVICE_CATEGORY_CPU,
     DEVICE_CATEGORY_MAX
 } DeviceCategory;
 
diff --git a/qdev-monitor.c b/qdev-monitor.c
index c73410c..5f2fcdf 100644
--- a/qdev-monitor.c
+++ b/qdev-monitor.c
@@ -136,6 +136,7 @@ static void qdev_print_devinfos(bool show_no_user)
         [DEVICE_CATEGORY_DISPLAY] = "Display",
         [DEVICE_CATEGORY_SOUND]   = "Sound",
         [DEVICE_CATEGORY_MISC]    = "Misc",
+        [DEVICE_CATEGORY_CPU]     = "CPU",
         [DEVICE_CATEGORY_MAX]     = "Uncategorized",
     };
     GSList *list, *elt;
diff --git a/qom/cpu.c b/qom/cpu.c
index 7f57587..e815db7 100644
--- a/qom/cpu.c
+++ b/qom/cpu.c
@@ -415,6 +415,7 @@ static void cpu_class_init(ObjectClass *klass, void *data)
     k->cpu_exec_enter = cpu_common_noop;
     k->cpu_exec_exit = cpu_common_noop;
     k->cpu_exec_interrupt = cpu_common_exec_interrupt;
+    set_bit(DEVICE_CATEGORY_CPU, dc->categories);
     dc->realize = cpu_common_realizefn;
     dc->unrealize = cpu_common_unrealizefn;
     /*
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [Qemu-devel] [PULL 15/41] hw/scsi: Fix debug message of cdb structure in scsi-generic
  2017-01-27 13:45 [Qemu-devel] [PULL 00/41] Misc changes for 2017-01-27 Paolo Bonzini
                   ` (13 preceding siblings ...)
  2017-01-27 13:45 ` [Qemu-devel] [PULL 14/41] Introduce DEVICE_CATEGORY_CPU for CPU devices Paolo Bonzini
@ 2017-01-27 13:45 ` Paolo Bonzini
  2017-01-27 13:45 ` [Qemu-devel] [PULL 16/41] block: Fix target variable of BLKSECTGET ioctl Paolo Bonzini
                   ` (26 subsequent siblings)
  41 siblings, 0 replies; 43+ messages in thread
From: Paolo Bonzini @ 2017-01-27 13:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: Eric Farman

From: Eric Farman <farman@linux.vnet.ibm.com>

When running with debug enabled, the scsi-generic cdb that is
dumped skips byte 0 of the command, which is the opcode.  This
makes identifying which command is being issued/completed a
little difficult.  Example:

  0x00 0x00 0x01 0x00 0x00
  scsi-generic: scsi_read_data 0x0
  scsi-generic: Data ready tag=0x0 len=164
  scsi-generic: scsi_read_data 0x0
  scsi-generic: Command complete 0x0x10a42c60 tag=0x0 status=0

Improve this by adding a message prior to the loop, similar to
what exists for scsi-disk.  Clean up a few other messages to be
more explicit of what is being represented.  Example:

  scsi-generic: Command: data=0x12 0x00 0x00 0x01 0x00 0x00
  scsi-generic: scsi_read_data tag=0x0
  scsi-generic: Data ready tag=0x0 len=164
  scsi-generic: scsi_read_data tag=0x0
  scsi-generic: Command complete 0x0x10a452d0 tag=0x0 status=0

Signed-off-by: Eric Farman <farman@linux.vnet.ibm.com>
Message-Id: <20170120162527.66075-2-farman@linux.vnet.ibm.com>
Reviewed-by: Fam Zheng <famz@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 hw/scsi/scsi-generic.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c
index 7a588a7..92f091a 100644
--- a/hw/scsi/scsi-generic.c
+++ b/hw/scsi/scsi-generic.c
@@ -246,7 +246,7 @@ static void scsi_read_data(SCSIRequest *req)
     SCSIDevice *s = r->req.dev;
     int ret;
 
-    DPRINTF("scsi_read_data 0x%x\n", req->tag);
+    DPRINTF("scsi_read_data tag=0x%x\n", req->tag);
 
     /* The request is used as the AIO opaque value, so add a ref.  */
     scsi_req_ref(&r->req);
@@ -294,7 +294,7 @@ static void scsi_write_data(SCSIRequest *req)
     SCSIDevice *s = r->req.dev;
     int ret;
 
-    DPRINTF("scsi_write_data 0x%x\n", req->tag);
+    DPRINTF("scsi_write_data tag=0x%x\n", req->tag);
     if (r->len == 0) {
         r->len = r->buflen;
         scsi_req_data(&r->req, r->len);
@@ -329,6 +329,7 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd)
     int ret;
 
 #ifdef DEBUG_SCSI
+    DPRINTF("Command: data=0x%02x", cmd[0]);
     {
         int i;
         for (i = 1; i < r->req.cmd.len; i++) {
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [Qemu-devel] [PULL 16/41] block: Fix target variable of BLKSECTGET ioctl
  2017-01-27 13:45 [Qemu-devel] [PULL 00/41] Misc changes for 2017-01-27 Paolo Bonzini
                   ` (14 preceding siblings ...)
  2017-01-27 13:45 ` [Qemu-devel] [PULL 15/41] hw/scsi: Fix debug message of cdb structure in scsi-generic Paolo Bonzini
@ 2017-01-27 13:45 ` Paolo Bonzini
  2017-01-27 13:45 ` [Qemu-devel] [PULL 17/41] block: get max_transfer limit for char (scsi-generic) devices Paolo Bonzini
                   ` (25 subsequent siblings)
  41 siblings, 0 replies; 43+ messages in thread
From: Paolo Bonzini @ 2017-01-27 13:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: Eric Farman

From: Eric Farman <farman@linux.vnet.ibm.com>

Commit 6f6071745bd0 ("raw-posix: Fetch max sectors for host block device")
introduced a routine to call the kernel BLKSECTGET ioctl, which stores the
result back to user space.  However, the size of the data returned depends
on the routine handling the ioctl.  The (compat_)blkdev_ioctl returns a
short, while sg_ioctl returns an int.  Thus, on big-endian systems, we can
find ourselves accidentally shifting the result to a much larger value.
(On s390x, a short is 16 bits while an int is 32 bits.)

Also, the two ioctl handlers return values in different scales (block
returns sectors, while sg returns bytes), so some tweaking of the outputs
is required such that hdev_get_max_transfer_length returns a value in a
consistent set of units.

Signed-off-by: Eric Farman <farman@linux.vnet.ibm.com>
Message-Id: <20170120162527.66075-3-farman@linux.vnet.ibm.com>
Reviewed-by: Fam Zheng <famz@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 block/file-posix.c    | 17 ++++++++++-------
 include/block/block.h |  1 +
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/block/file-posix.c b/block/file-posix.c
index 28b47d9..9f83725 100644
--- a/block/file-posix.c
+++ b/block/file-posix.c
@@ -651,12 +651,15 @@ static void raw_reopen_abort(BDRVReopenState *state)
     state->opaque = NULL;
 }
 
-static int hdev_get_max_transfer_length(int fd)
+static int hdev_get_max_transfer_length(BlockDriverState *bs, int fd)
 {
 #ifdef BLKSECTGET
-    int max_sectors = 0;
-    if (ioctl(fd, BLKSECTGET, &max_sectors) == 0) {
-        return max_sectors;
+    int max_bytes = 0;
+    short max_sectors = 0;
+    if (bs->sg && ioctl(fd, BLKSECTGET, &max_bytes) == 0) {
+        return max_bytes;
+    } else if (!bs->sg && ioctl(fd, BLKSECTGET, &max_sectors) == 0) {
+        return max_sectors << BDRV_SECTOR_BITS;
     } else {
         return -errno;
     }
@@ -672,9 +675,9 @@ static void raw_refresh_limits(BlockDriverState *bs, Error **errp)
 
     if (!fstat(s->fd, &st)) {
         if (S_ISBLK(st.st_mode)) {
-            int ret = hdev_get_max_transfer_length(s->fd);
-            if (ret > 0 && ret <= BDRV_REQUEST_MAX_SECTORS) {
-                bs->bl.max_transfer = pow2floor(ret << BDRV_SECTOR_BITS);
+            int ret = hdev_get_max_transfer_length(bs, s->fd);
+            if (ret > 0 && ret <= BDRV_REQUEST_MAX_BYTES) {
+                bs->bl.max_transfer = pow2floor(ret);
             }
         }
     }
diff --git a/include/block/block.h b/include/block/block.h
index 8b0dcda..4e81f20 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -116,6 +116,7 @@ typedef struct HDGeometry {
 
 #define BDRV_REQUEST_MAX_SECTORS MIN(SIZE_MAX >> BDRV_SECTOR_BITS, \
                                      INT_MAX >> BDRV_SECTOR_BITS)
+#define BDRV_REQUEST_MAX_BYTES (BDRV_REQUEST_MAX_SECTORS << BDRV_SECTOR_BITS)
 
 /*
  * Allocation status flags
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [Qemu-devel] [PULL 17/41] block: get max_transfer limit for char (scsi-generic) devices
  2017-01-27 13:45 [Qemu-devel] [PULL 00/41] Misc changes for 2017-01-27 Paolo Bonzini
                   ` (15 preceding siblings ...)
  2017-01-27 13:45 ` [Qemu-devel] [PULL 16/41] block: Fix target variable of BLKSECTGET ioctl Paolo Bonzini
@ 2017-01-27 13:45 ` Paolo Bonzini
  2017-01-27 13:45 ` [Qemu-devel] [PULL 18/41] x86-KVM: Supply TSC and APIC clock rates to guest like VMWare Paolo Bonzini
                   ` (24 subsequent siblings)
  41 siblings, 0 replies; 43+ messages in thread
From: Paolo Bonzini @ 2017-01-27 13:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: Eric Farman

From: Eric Farman <farman@linux.vnet.ibm.com>

We can get the maximum number of bytes for a single I/O transfer
from the BLKSECTGET ioctl, but we only perform this for block
devices.  scsi-generic devices are represented as character devices,
and so do not issue this today.  Update this, so that virtio-scsi
devices using the scsi-generic interface can return the same data.

Signed-off-by: Eric Farman <farman@linux.vnet.ibm.com>
Message-Id: <20170120162527.66075-4-farman@linux.vnet.ibm.com>
Reviewed-by: Fam Zheng <famz@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 block/file-posix.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/block/file-posix.c b/block/file-posix.c
index 9f83725..2134e0e 100644
--- a/block/file-posix.c
+++ b/block/file-posix.c
@@ -674,7 +674,7 @@ static void raw_refresh_limits(BlockDriverState *bs, Error **errp)
     struct stat st;
 
     if (!fstat(s->fd, &st)) {
-        if (S_ISBLK(st.st_mode)) {
+        if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode)) {
             int ret = hdev_get_max_transfer_length(bs, s->fd);
             if (ret > 0 && ret <= BDRV_REQUEST_MAX_BYTES) {
                 bs->bl.max_transfer = pow2floor(ret);
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [Qemu-devel] [PULL 18/41] x86-KVM: Supply TSC and APIC clock rates to guest like VMWare
  2017-01-27 13:45 [Qemu-devel] [PULL 00/41] Misc changes for 2017-01-27 Paolo Bonzini
                   ` (16 preceding siblings ...)
  2017-01-27 13:45 ` [Qemu-devel] [PULL 17/41] block: get max_transfer limit for char (scsi-generic) devices Paolo Bonzini
@ 2017-01-27 13:45 ` Paolo Bonzini
  2017-01-27 13:45 ` [Qemu-devel] [PULL 19/41] pc: Enable vmware-cpuid-freq CPU option for 2.9+ machine types Paolo Bonzini
                   ` (23 subsequent siblings)
  41 siblings, 0 replies; 43+ messages in thread
From: Paolo Bonzini @ 2017-01-27 13:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: Phil Dennis-Jordan

From: Phil Dennis-Jordan <phil@philjordan.eu>

This fixes timekeeping of x86-64 Darwin/OS X/macOS guests when using KVM.

Darwin/OS X/macOS for x86-64 uses the TSC for timekeeping; it normally calibrates this by querying various clock frequency scaling MSRs. Details depend on the exact CPU model detected. The local APIC timer frequency is extracted from (EFI) firmware.

This is problematic in the presence of virtualisation, as the MSRs in question are typically not handled by the hypervisor. VMWare (Fusion) advertises TSC and APIC frequency via a custom 0x40000010 CPUID leaf, in the eax and ebx registers respectively. This is documented at https://lwn.net/Articles/301888/ among other places.

Darwin/OS X/macOS looks for the generic 0x40000000 hypervisor leaf, and if this indicates via eax that leaf 0x40000010 might be available, that is in turn queried for the two frequencies.

This adds a CPU option "vmware-cpuid-freq" to enable the same behaviour when running Qemu with KVM acceleration, if the KVM TSC frequency can be determined, and it is stable. (invtsc or user-specified) The virtualised APIC bus cycle is hardcoded to 1GHz in KVM, so ebx of the CPUID leaf is also hardcoded to this value.

Signed-off-by: Phil Dennis-Jordan <phil@philjordan.eu>
Message-Id: <1484921496-11257-2-git-send-email-phil@philjordan.eu>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 target/i386/cpu.c |  1 +
 target/i386/cpu.h |  4 ++++
 target/i386/kvm.c | 36 ++++++++++++++++++++++++++++++------
 3 files changed, 35 insertions(+), 6 deletions(-)

diff --git a/target/i386/cpu.c b/target/i386/cpu.c
index aba11ae..dabad37 100644
--- a/target/i386/cpu.c
+++ b/target/i386/cpu.c
@@ -3677,6 +3677,7 @@ static Property x86_cpu_properties[] = {
     DEFINE_PROP_BOOL("cpuid-0xb", X86CPU, enable_cpuid_0xb, true),
     DEFINE_PROP_BOOL("lmce", X86CPU, enable_lmce, false),
     DEFINE_PROP_BOOL("l3-cache", X86CPU, enable_l3_cache, true),
+    DEFINE_PROP_BOOL("vmware-cpuid-freq", X86CPU, vmware_cpuid_freq, false),
     DEFINE_PROP_END_OF_LIST()
 };
 
diff --git a/target/i386/cpu.h b/target/i386/cpu.h
index 6c1902b..d51b892 100644
--- a/target/i386/cpu.h
+++ b/target/i386/cpu.h
@@ -1213,6 +1213,10 @@ struct X86CPU {
     bool host_features;
     uint32_t apic_id;
 
+    /* Enables publishing of TSC increment and Local APIC bus frequencies to
+     * the guest OS in CPUID page 0x40000010, the same way that VMWare does. */
+    bool vmware_cpuid_freq;
+
     /* if true the CPUID code directly forward host cache leaves to the guest */
     bool cache_info_passthrough;
 
diff --git a/target/i386/kvm.c b/target/i386/kvm.c
index e6c4f75..00e9080 100644
--- a/target/i386/kvm.c
+++ b/target/i386/kvm.c
@@ -974,12 +974,6 @@ int kvm_arch_init_vcpu(CPUState *cs)
         vmstate_x86_cpu.unmigratable = 1;
     }
 
-    cpuid_data.cpuid.padding = 0;
-    r = kvm_vcpu_ioctl(cs, KVM_SET_CPUID2, &cpuid_data);
-    if (r) {
-        return r;
-    }
-
     r = kvm_arch_set_tsc_khz(cs);
     if (r < 0) {
         return r;
@@ -999,6 +993,36 @@ int kvm_arch_init_vcpu(CPUState *cs)
         }
     }
 
+    if (cpu->vmware_cpuid_freq
+        /* Guests depend on 0x40000000 to detect this feature, so only expose
+         * it if KVM exposes leaf 0x40000000. (Conflicts with Hyper-V) */
+        && cpu->expose_kvm
+        && kvm_base == KVM_CPUID_SIGNATURE
+        /* TSC clock must be stable and known for this feature. */
+        && ((env->features[FEAT_8000_0007_EDX] & CPUID_APM_INVTSC)
+            || env->user_tsc_khz != 0)
+        && env->tsc_khz != 0) {
+
+        c = &cpuid_data.entries[cpuid_i++];
+        c->function = KVM_CPUID_SIGNATURE | 0x10;
+        c->eax = env->tsc_khz;
+        /* LAPIC resolution of 1ns (freq: 1GHz) is hardcoded in KVM's
+         * APIC_BUS_CYCLE_NS */
+        c->ebx = 1000000;
+        c->ecx = c->edx = 0;
+
+        c = cpuid_find_entry(&cpuid_data.cpuid, kvm_base, 0);
+        c->eax = MAX(c->eax, KVM_CPUID_SIGNATURE | 0x10);
+    }
+
+    cpuid_data.cpuid.nent = cpuid_i;
+
+    cpuid_data.cpuid.padding = 0;
+    r = kvm_vcpu_ioctl(cs, KVM_SET_CPUID2, &cpuid_data);
+    if (r) {
+        return r;
+    }
+
     if (has_xsave) {
         env->kvm_xsave_buf = qemu_memalign(4096, sizeof(struct kvm_xsave));
     }
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [Qemu-devel] [PULL 19/41] pc: Enable vmware-cpuid-freq CPU option for 2.9+ machine types
  2017-01-27 13:45 [Qemu-devel] [PULL 00/41] Misc changes for 2017-01-27 Paolo Bonzini
                   ` (17 preceding siblings ...)
  2017-01-27 13:45 ` [Qemu-devel] [PULL 18/41] x86-KVM: Supply TSC and APIC clock rates to guest like VMWare Paolo Bonzini
@ 2017-01-27 13:45 ` Paolo Bonzini
  2017-01-27 13:45 ` [Qemu-devel] [PULL 20/41] block/iscsi: statically link qemu_iscsi_opts Paolo Bonzini
                   ` (22 subsequent siblings)
  41 siblings, 0 replies; 43+ messages in thread
From: Paolo Bonzini @ 2017-01-27 13:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: Phil Dennis-Jordan

From: Phil Dennis-Jordan <phil@philjordan.eu>

Signed-off-by: Phil Dennis-Jordan <phil@philjordan.eu>
Message-Id: <1484921496-11257-4-git-send-email-phil@philjordan.eu>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 include/hw/i386/pc.h | 6 +++++-
 target/i386/cpu.c    | 2 +-
 2 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
index 020b632..e34ab30 100644
--- a/include/hw/i386/pc.h
+++ b/include/hw/i386/pc.h
@@ -386,7 +386,11 @@ bool e820_get_entry(int, uint32_t, uint64_t *, uint64_t *);
         .property = "x-smi-broadcast",\
         .value    = "off",\
     },\
-
+    {\
+        .driver   = TYPE_X86_CPU,\
+        .property = "vmware-cpuid-freq",\
+        .value    = "off",\
+    },
 
 #define PC_COMPAT_2_7 \
     HW_COMPAT_2_7 \
diff --git a/target/i386/cpu.c b/target/i386/cpu.c
index dabad37..feded98 100644
--- a/target/i386/cpu.c
+++ b/target/i386/cpu.c
@@ -3677,7 +3677,7 @@ static Property x86_cpu_properties[] = {
     DEFINE_PROP_BOOL("cpuid-0xb", X86CPU, enable_cpuid_0xb, true),
     DEFINE_PROP_BOOL("lmce", X86CPU, enable_lmce, false),
     DEFINE_PROP_BOOL("l3-cache", X86CPU, enable_l3_cache, true),
-    DEFINE_PROP_BOOL("vmware-cpuid-freq", X86CPU, vmware_cpuid_freq, false),
+    DEFINE_PROP_BOOL("vmware-cpuid-freq", X86CPU, vmware_cpuid_freq, true),
     DEFINE_PROP_END_OF_LIST()
 };
 
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [Qemu-devel] [PULL 20/41] block/iscsi: statically link qemu_iscsi_opts
  2017-01-27 13:45 [Qemu-devel] [PULL 00/41] Misc changes for 2017-01-27 Paolo Bonzini
                   ` (18 preceding siblings ...)
  2017-01-27 13:45 ` [Qemu-devel] [PULL 19/41] pc: Enable vmware-cpuid-freq CPU option for 2.9+ machine types Paolo Bonzini
@ 2017-01-27 13:45 ` Paolo Bonzini
  2017-01-27 13:45 ` [Qemu-devel] [PULL 21/41] tests: fix linking test-char on win32 Paolo Bonzini
                   ` (21 subsequent siblings)
  41 siblings, 0 replies; 43+ messages in thread
From: Paolo Bonzini @ 2017-01-27 13:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: Peter Lieven, qemu-stable

From: Peter Lieven <pl@kamp.de>

commit f57b4b5f moved qemu_iscsi_opts into vl.c. This
made them invisible for qemu-img, qemu-nbd etc.

Fixes: f57b4b5fb127b60e1aade2684a8b16bc4f630b29
Cc: qemu-stable@nongnu.org
Signed-off-by: Peter Lieven <pl@kamp.de>
Message-Id: <1485262161-18543-1-git-send-email-pl@kamp.de>
[Drop useless #ifdef. - Paolo]
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 MAINTAINERS         |  1 +
 block/Makefile.objs |  1 +
 block/iscsi-opts.c  | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 vl.c                | 40 -------------------------------
 4 files changed, 71 insertions(+), 40 deletions(-)
 create mode 100644 block/iscsi-opts.c

diff --git a/MAINTAINERS b/MAINTAINERS
index ad10004..dc779e9 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1626,6 +1626,7 @@ M: Peter Lieven <pl@kamp.de>
 L: qemu-block@nongnu.org
 S: Supported
 F: block/iscsi.c
+F: block/iscsi-opts.c
 
 NFS
 M: Jeff Cody <jcody@redhat.com>
diff --git a/block/Makefile.objs b/block/Makefile.objs
index 0b8fd06..c6bd14e 100644
--- a/block/Makefile.objs
+++ b/block/Makefile.objs
@@ -14,6 +14,7 @@ block-obj-y += throttle-groups.o
 
 block-obj-y += nbd.o nbd-client.o sheepdog.o
 block-obj-$(CONFIG_LIBISCSI) += iscsi.o
+block-obj-$(if $(CONFIG_LIBISCSI),y,n) += iscsi-opts.o
 block-obj-$(CONFIG_LIBNFS) += nfs.o
 block-obj-$(CONFIG_CURL) += curl.o
 block-obj-$(CONFIG_RBD) += rbd.o
diff --git a/block/iscsi-opts.c b/block/iscsi-opts.c
new file mode 100644
index 0000000..5335539
--- /dev/null
+++ b/block/iscsi-opts.c
@@ -0,0 +1,69 @@
+/*
+ * QEMU Block driver for iSCSI images (static options)
+ *
+ * Copyright (c) 2017 Peter Lieven <pl@kamp.de>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "qemu/config-file.h"
+
+static QemuOptsList qemu_iscsi_opts = {
+    .name = "iscsi",
+    .head = QTAILQ_HEAD_INITIALIZER(qemu_iscsi_opts.head),
+    .desc = {
+        {
+            .name = "user",
+            .type = QEMU_OPT_STRING,
+            .help = "username for CHAP authentication to target",
+        },{
+            .name = "password",
+            .type = QEMU_OPT_STRING,
+            .help = "password for CHAP authentication to target",
+        },{
+            .name = "password-secret",
+            .type = QEMU_OPT_STRING,
+            .help = "ID of the secret providing password for CHAP "
+                    "authentication to target",
+        },{
+            .name = "header-digest",
+            .type = QEMU_OPT_STRING,
+            .help = "HeaderDigest setting. "
+                    "{CRC32C|CRC32C-NONE|NONE-CRC32C|NONE}",
+        },{
+            .name = "initiator-name",
+            .type = QEMU_OPT_STRING,
+            .help = "Initiator iqn name to use when connecting",
+        },{
+            .name = "timeout",
+            .type = QEMU_OPT_NUMBER,
+            .help = "Request timeout in seconds (default 0 = no timeout)",
+        },
+        { /* end of list */ }
+    },
+};
+
+static void iscsi_block_opts_init(void)
+{
+    qemu_add_opts(&qemu_iscsi_opts);
+}
+
+block_init(iscsi_block_opts_init);
diff --git a/vl.c b/vl.c
index 1f39cc0..bc8e837 100644
--- a/vl.c
+++ b/vl.c
@@ -514,43 +514,6 @@ static QemuOptsList qemu_fw_cfg_opts = {
     },
 };
 
-#ifdef CONFIG_LIBISCSI
-static QemuOptsList qemu_iscsi_opts = {
-    .name = "iscsi",
-    .head = QTAILQ_HEAD_INITIALIZER(qemu_iscsi_opts.head),
-    .desc = {
-        {
-            .name = "user",
-            .type = QEMU_OPT_STRING,
-            .help = "username for CHAP authentication to target",
-        },{
-            .name = "password",
-            .type = QEMU_OPT_STRING,
-            .help = "password for CHAP authentication to target",
-        },{
-            .name = "password-secret",
-            .type = QEMU_OPT_STRING,
-            .help = "ID of the secret providing password for CHAP "
-                    "authentication to target",
-        },{
-            .name = "header-digest",
-            .type = QEMU_OPT_STRING,
-            .help = "HeaderDigest setting. "
-                    "{CRC32C|CRC32C-NONE|NONE-CRC32C|NONE}",
-        },{
-            .name = "initiator-name",
-            .type = QEMU_OPT_STRING,
-            .help = "Initiator iqn name to use when connecting",
-        },{
-            .name = "timeout",
-            .type = QEMU_OPT_NUMBER,
-            .help = "Request timeout in seconds (default 0 = no timeout)",
-        },
-        { /* end of list */ }
-    },
-};
-#endif
-
 /**
  * Get machine options
  *
@@ -3032,9 +2995,6 @@ int main(int argc, char **argv, char **envp)
     qemu_add_opts(&qemu_icount_opts);
     qemu_add_opts(&qemu_semihosting_config_opts);
     qemu_add_opts(&qemu_fw_cfg_opts);
-#ifdef CONFIG_LIBISCSI
-    qemu_add_opts(&qemu_iscsi_opts);
-#endif
     module_call_init(MODULE_INIT_OPTS);
 
     runstate_init();
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [Qemu-devel] [PULL 21/41] tests: fix linking test-char on win32
  2017-01-27 13:45 [Qemu-devel] [PULL 00/41] Misc changes for 2017-01-27 Paolo Bonzini
                   ` (19 preceding siblings ...)
  2017-01-27 13:45 ` [Qemu-devel] [PULL 20/41] block/iscsi: statically link qemu_iscsi_opts Paolo Bonzini
@ 2017-01-27 13:45 ` Paolo Bonzini
  2017-01-27 13:45 ` [Qemu-devel] [PULL 22/41] qemu-options: stdio is available " Paolo Bonzini
                   ` (20 subsequent siblings)
  41 siblings, 0 replies; 43+ messages in thread
From: Paolo Bonzini @ 2017-01-27 13:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau

From: Marc-André Lureau <marcandre.lureau@redhat.com>

test.char.exe fails to link:
qemu-char.o: In function `win_chr_free':
/home/elmarco/src/qemu/qemu-char.c:2149: undefined reference to `qemu_del_polling_cb'
/home/elmarco/src/qemu/qemu-char.c:2151: undefined reference to `qemu_del_polling_cb'
qemu-char.o: In function `win_stdio_thread':
/home/elmarco/src/qemu/qemu-char.c:2568: undefined reference to `qemu_del_wait_object'
qemu-char.o: In function `qemu_chr_open_stdio':
/home/elmarco/src/qemu/qemu-char.c:2661: undefined reference to `qemu_add_wait_object'
/home/elmarco/src/qemu/qemu-char.c:2646: undefined reference to
`qemu_add_wait_object'
...

It needs main-loop.o symbols, among others. Linking with
$(test-block-obj-y) brings what's necessary. We could try to eventually
strip to the minimum if needed.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 tests/Makefile.include | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/Makefile.include b/tests/Makefile.include
index 22ea256..33b4f88 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -510,7 +510,7 @@ tests/check-qjson$(EXESUF): tests/check-qjson.o $(test-util-obj-y)
 tests/check-qom-interface$(EXESUF): tests/check-qom-interface.o $(test-qom-obj-y)
 tests/check-qom-proplist$(EXESUF): tests/check-qom-proplist.o $(test-qom-obj-y)
 
-tests/test-char$(EXESUF): tests/test-char.o qemu-char.o qemu-timer.o $(test-util-obj-y) $(qtest-obj-y) $(test-io-obj-y)
+tests/test-char$(EXESUF): tests/test-char.o qemu-char.o qemu-timer.o $(test-util-obj-y) $(qtest-obj-y) $(test-block-obj-y)
 tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(test-block-obj-y)
 tests/test-aio$(EXESUF): tests/test-aio.o $(test-block-obj-y)
 tests/test-throttle$(EXESUF): tests/test-throttle.o $(test-block-obj-y)
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [Qemu-devel] [PULL 22/41] qemu-options: stdio is available on win32
  2017-01-27 13:45 [Qemu-devel] [PULL 00/41] Misc changes for 2017-01-27 Paolo Bonzini
                   ` (20 preceding siblings ...)
  2017-01-27 13:45 ` [Qemu-devel] [PULL 21/41] tests: fix linking test-char on win32 Paolo Bonzini
@ 2017-01-27 13:45 ` Paolo Bonzini
  2017-01-27 13:45 ` [Qemu-devel] [PULL 23/41] char: add qemu_chr_fe_add_watch() Returns description Paolo Bonzini
                   ` (19 subsequent siblings)
  41 siblings, 0 replies; 43+ messages in thread
From: Paolo Bonzini @ 2017-01-27 13:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Available since commit db418a0a7ef5887ea0f3d167584e6f500bb0c4c5
(October 2011, qemu 1.0)

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 qemu-options.hx | 2 --
 1 file changed, 2 deletions(-)

diff --git a/qemu-options.hx b/qemu-options.hx
index 1b4f7d6..a23a137 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -2431,8 +2431,6 @@ Connect to standard input and standard output of the QEMU process.
 exiting QEMU with the key sequence @key{Control-c}. This option is enabled by
 default, use @option{signal=off} to disable it.
 
-@option{stdio} is not available on Windows hosts.
-
 @item -chardev braille ,id=@var{id}
 
 Connect to a local BrlAPI server. @option{braille} does not take any options.
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [Qemu-devel] [PULL 23/41] char: add qemu_chr_fe_add_watch() Returns description
  2017-01-27 13:45 [Qemu-devel] [PULL 00/41] Misc changes for 2017-01-27 Paolo Bonzini
                   ` (21 preceding siblings ...)
  2017-01-27 13:45 ` [Qemu-devel] [PULL 22/41] qemu-options: stdio is available " Paolo Bonzini
@ 2017-01-27 13:45 ` Paolo Bonzini
  2017-01-27 13:45 ` [Qemu-devel] [PULL 24/41] doc: fix spelling Paolo Bonzini
                   ` (18 subsequent siblings)
  41 siblings, 0 replies; 43+ messages in thread
From: Paolo Bonzini @ 2017-01-27 13:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 include/sysemu/char.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/include/sysemu/char.h b/include/sysemu/char.h
index 0a14942..b6e3618 100644
--- a/include/sysemu/char.h
+++ b/include/sysemu/char.h
@@ -258,6 +258,8 @@ void qemu_chr_fe_printf(CharBackend *be, const char *fmt, ...)
  * @cond the condition to poll for
  * @func the function to call when the condition happens
  * @user_data the opaque pointer to pass to @func
+ *
+ * Returns: the source tag
  */
 guint qemu_chr_fe_add_watch(CharBackend *be, GIOCondition cond,
                             GIOFunc func, void *user_data);
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [Qemu-devel] [PULL 24/41] doc: fix spelling
  2017-01-27 13:45 [Qemu-devel] [PULL 00/41] Misc changes for 2017-01-27 Paolo Bonzini
                   ` (22 preceding siblings ...)
  2017-01-27 13:45 ` [Qemu-devel] [PULL 23/41] char: add qemu_chr_fe_add_watch() Returns description Paolo Bonzini
@ 2017-01-27 13:45 ` Paolo Bonzini
  2017-01-27 13:45 ` [Qemu-devel] [PULL 25/41] char: use a const CharDriver Paolo Bonzini
                   ` (17 subsequent siblings)
  41 siblings, 0 replies; 43+ messages in thread
From: Paolo Bonzini @ 2017-01-27 13:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau

From: Marc-André Lureau <marcandre.lureau@redhat.com>

I am pretty sure that's the word Fabrice Bellard intended to write.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 qemu-options.hx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/qemu-options.hx b/qemu-options.hx
index a23a137..34ece80 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -3016,7 +3016,7 @@ udp::4555@@:4556} to QEMU. Another approach is to use a patched
 version of netcat which can listen to a TCP port and send and receive
 characters via udp.  If you have a patched version of netcat which
 activates telnet remote echo and single char transfer, then you can
-use the following options to step up a netcat redirector to allow
+use the following options to set up a netcat redirector to allow
 telnet on port 5555 to access the QEMU port.
 @table @code
 @item QEMU Options:
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [Qemu-devel] [PULL 25/41] char: use a const CharDriver
  2017-01-27 13:45 [Qemu-devel] [PULL 00/41] Misc changes for 2017-01-27 Paolo Bonzini
                   ` (23 preceding siblings ...)
  2017-01-27 13:45 ` [Qemu-devel] [PULL 24/41] doc: fix spelling Paolo Bonzini
@ 2017-01-27 13:45 ` Paolo Bonzini
  2017-01-27 13:45 ` [Qemu-devel] [PULL 26/41] char: use a static array for backends Paolo Bonzini
                   ` (16 subsequent siblings)
  41 siblings, 0 replies; 43+ messages in thread
From: Paolo Bonzini @ 2017-01-27 13:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau

From: Marc-André Lureau <marcandre.lureau@redhat.com>

No need to allocate & copy fields, let's use static const struct instead.

Add an alias field to the CharDriver structure to cover the cases where
we previously registered a driver twice under two names.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 backends/baum.c       |   8 ++-
 backends/msmouse.c    |   7 ++-
 backends/testdev.c    |   7 ++-
 include/sysemu/char.h |  20 +++---
 qemu-char.c           | 169 +++++++++++++++++++++++++++++++-------------------
 spice-qemu-char.c     |  16 +++--
 ui/console.c          |  10 ++-
 7 files changed, 151 insertions(+), 86 deletions(-)

diff --git a/backends/baum.c b/backends/baum.c
index b045ef4..2e404a1 100644
--- a/backends/baum.c
+++ b/backends/baum.c
@@ -671,8 +671,12 @@ fail_handle:
 
 static void register_types(void)
 {
-    register_char_driver("braille", CHARDEV_BACKEND_KIND_BRAILLE, NULL,
-                         chr_baum_init);
+    static const CharDriver driver = {
+        .kind = CHARDEV_BACKEND_KIND_BRAILLE,
+        .create = chr_baum_init,
+    };
+
+    register_char_driver(&driver);
 }
 
 type_init(register_types);
diff --git a/backends/msmouse.c b/backends/msmouse.c
index 733ca80..2490b2c 100644
--- a/backends/msmouse.c
+++ b/backends/msmouse.c
@@ -179,8 +179,11 @@ static CharDriverState *qemu_chr_open_msmouse(const char *id,
 
 static void register_types(void)
 {
-    register_char_driver("msmouse", CHARDEV_BACKEND_KIND_MSMOUSE, NULL,
-                         qemu_chr_open_msmouse);
+    static const CharDriver driver = {
+        .kind = CHARDEV_BACKEND_KIND_MSMOUSE,
+        .create = qemu_chr_open_msmouse,
+    };
+    register_char_driver(&driver);
 }
 
 type_init(register_types);
diff --git a/backends/testdev.c b/backends/testdev.c
index 60156e3..cd25094 100644
--- a/backends/testdev.c
+++ b/backends/testdev.c
@@ -130,8 +130,11 @@ static CharDriverState *chr_testdev_init(const char *id,
 
 static void register_types(void)
 {
-    register_char_driver("testdev", CHARDEV_BACKEND_KIND_TESTDEV, NULL,
-                         chr_testdev_init);
+    static const CharDriver driver = {
+        .kind = CHARDEV_BACKEND_KIND_TESTDEV,
+        .create = chr_testdev_init,
+    };
+    register_char_driver(&driver);
 }
 
 type_init(register_types);
diff --git a/include/sysemu/char.h b/include/sysemu/char.h
index b6e3618..fee2c34 100644
--- a/include/sysemu/char.h
+++ b/include/sysemu/char.h
@@ -475,15 +475,17 @@ void qemu_chr_set_feature(CharDriverState *chr,
                           CharDriverFeature feature);
 QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename);
 
-typedef void CharDriverParse(QemuOpts *opts, ChardevBackend *backend,
-                             Error **errp);
-typedef CharDriverState *CharDriverCreate(const char *id,
-                                          ChardevBackend *backend,
-                                          ChardevReturn *ret, bool *be_opened,
-                                          Error **errp);
-
-void register_char_driver(const char *name, ChardevBackendKind kind,
-                          CharDriverParse *parse, CharDriverCreate *create);
+typedef struct CharDriver {
+    ChardevBackendKind kind;
+    const char *alias;
+    void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp);
+    CharDriverState *(*create)(const char *id,
+                               ChardevBackend *backend,
+                               ChardevReturn *ret, bool *be_opened,
+                               Error **errp);
+} CharDriver;
+
+void register_char_driver(const CharDriver *driver);
 
 extern int term_escape_char;
 
diff --git a/qemu-char.c b/qemu-char.c
index d8da167..51558a7 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -4101,27 +4101,12 @@ static void qemu_chr_parse_udp(QemuOpts *opts, ChardevBackend *backend,
     }
 }
 
-typedef struct CharDriver {
-    const char *name;
-    ChardevBackendKind kind;
-    CharDriverParse *parse;
-    CharDriverCreate *create;
-} CharDriver;
-
 static GSList *backends;
 
-void register_char_driver(const char *name, ChardevBackendKind kind,
-                          CharDriverParse *parse, CharDriverCreate *create)
+void register_char_driver(const CharDriver *driver)
 {
-    CharDriver *s;
-
-    s = g_malloc0(sizeof(*s));
-    s->name = g_strdup(name);
-    s->kind = kind;
-    s->parse = parse;
-    s->create = create;
-
-    backends = g_slist_append(backends, s);
+    /* casting away const */
+    backends = g_slist_append(backends, (void *)driver);
 }
 
 CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
@@ -4133,22 +4118,26 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
     GSList *i;
     ChardevReturn *ret = NULL;
     ChardevBackend *backend;
+    const char *name = qemu_opt_get(opts, "backend");
     const char *id = qemu_opts_id(opts);
     char *bid = NULL;
 
-    if (qemu_opt_get(opts, "backend") == NULL) {
+    if (name == NULL) {
         error_setg(errp, "chardev: \"%s\" missing backend",
                    qemu_opts_id(opts));
         goto err;
     }
 
-    if (is_help_option(qemu_opt_get(opts, "backend"))) {
+    if (is_help_option(name)) {
         fprintf(stderr, "Available chardev backend types:\n");
         for (i = backends; i; i = i->next) {
             cd = i->data;
-            fprintf(stderr, "%s\n", cd->name);
+            fprintf(stderr, "%s\n", ChardevBackendKind_lookup[cd->kind]);
+            if (cd->alias) {
+                fprintf(stderr, "%s\n", cd->alias);
+            }
         }
-        exit(!is_help_option(qemu_opt_get(opts, "backend")));
+        exit(0);
     }
 
     if (id == NULL) {
@@ -4159,13 +4148,13 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
     for (i = backends; i; i = i->next) {
         cd = i->data;
 
-        if (strcmp(cd->name, qemu_opt_get(opts, "backend")) == 0) {
+        if (g_strcmp0(ChardevBackendKind_lookup[cd->kind], name) == 0 ||
+            g_strcmp0(cd->alias, name) == 0) {
             break;
         }
     }
     if (i == NULL) {
-        error_setg(errp, "chardev: backend \"%s\" not found",
-                   qemu_opt_get(opts, "backend"));
+        error_setg(errp, "chardev: backend \"%s\" not found", name);
         goto err;
     }
 
@@ -4368,20 +4357,29 @@ ChardevInfoList *qmp_query_chardev(Error **errp)
     return chr_list;
 }
 
+static ChardevBackendInfoList *
+qmp_prepend_backend(ChardevBackendInfoList *list, const char *name)
+{
+    ChardevBackendInfoList *info = g_malloc0(sizeof(*info));
+    info->value = g_malloc0(sizeof(*info->value));
+    info->value->name = g_strdup(name);
+    info->next = list;
+    return info;
+}
+
 ChardevBackendInfoList *qmp_query_chardev_backends(Error **errp)
 {
     ChardevBackendInfoList *backend_list = NULL;
-    CharDriver *c = NULL;
-    GSList *i = NULL;
+    CharDriver *c;
+    GSList *i;
 
     for (i = backends; i; i = i->next) {
-        ChardevBackendInfoList *info = g_malloc0(sizeof(*info));
         c = i->data;
-        info->value = g_malloc0(sizeof(*info->value));
-        info->value->name = g_strdup(c->name);
-
-        info->next = backend_list;
-        backend_list = info;
+        backend_list = qmp_prepend_backend(backend_list,
+                                           ChardevBackendKind_lookup[c->kind]);
+        if (c->alias) {
+            backend_list = qmp_prepend_backend(backend_list, c->alias);
+        }
     }
 
     return backend_list;
@@ -4914,45 +4912,88 @@ void qemu_chr_cleanup(void)
 
 static void register_types(void)
 {
-    register_char_driver("null", CHARDEV_BACKEND_KIND_NULL, NULL,
-                         qemu_chr_open_null);
-    register_char_driver("socket", CHARDEV_BACKEND_KIND_SOCKET,
-                         qemu_chr_parse_socket, qmp_chardev_open_socket);
-    register_char_driver("udp", CHARDEV_BACKEND_KIND_UDP, qemu_chr_parse_udp,
-                         qmp_chardev_open_udp);
-    register_char_driver("ringbuf", CHARDEV_BACKEND_KIND_RINGBUF,
-                         qemu_chr_parse_ringbuf, qemu_chr_open_ringbuf);
-    register_char_driver("file", CHARDEV_BACKEND_KIND_FILE,
-                         qemu_chr_parse_file_out, qmp_chardev_open_file);
-    register_char_driver("stdio", CHARDEV_BACKEND_KIND_STDIO,
-                         qemu_chr_parse_stdio, qemu_chr_open_stdio);
+    int i;
+    static const CharDriver drivers[] = {
+        {
+            .kind = CHARDEV_BACKEND_KIND_NULL,
+            .create = qemu_chr_open_null,
+        },
+        {
+            .kind = CHARDEV_BACKEND_KIND_SOCKET,
+            .parse = qemu_chr_parse_socket,
+            .create = qmp_chardev_open_socket,
+        },
+        {
+            .kind = CHARDEV_BACKEND_KIND_UDP,
+            .parse = qemu_chr_parse_udp,
+            .create = qmp_chardev_open_udp,
+        },
+        {
+            .kind = CHARDEV_BACKEND_KIND_RINGBUF,
+            .parse = qemu_chr_parse_ringbuf,
+            .create = qemu_chr_open_ringbuf,
+        },
+        {
+            .kind = CHARDEV_BACKEND_KIND_FILE,
+            .parse = qemu_chr_parse_file_out,
+            .create = qmp_chardev_open_file,
+        },
+        {
+            .kind = CHARDEV_BACKEND_KIND_STDIO,
+            .parse = qemu_chr_parse_stdio,
+            .create = qemu_chr_open_stdio,
+        },
 #if defined HAVE_CHARDEV_SERIAL
-    register_char_driver("serial", CHARDEV_BACKEND_KIND_SERIAL,
-                         qemu_chr_parse_serial, qmp_chardev_open_serial);
-    register_char_driver("tty", CHARDEV_BACKEND_KIND_SERIAL,
-                         qemu_chr_parse_serial, qmp_chardev_open_serial);
+        {
+            .kind = CHARDEV_BACKEND_KIND_SERIAL,
+            .alias = "tty",
+            .parse = qemu_chr_parse_serial,
+            .create = qmp_chardev_open_serial,
+        },
 #endif
 #ifdef HAVE_CHARDEV_PARPORT
-    register_char_driver("parallel", CHARDEV_BACKEND_KIND_PARALLEL,
-                         qemu_chr_parse_parallel, qmp_chardev_open_parallel);
-    register_char_driver("parport", CHARDEV_BACKEND_KIND_PARALLEL,
-                         qemu_chr_parse_parallel, qmp_chardev_open_parallel);
+        {
+            .kind = CHARDEV_BACKEND_KIND_PARALLEL,
+            .alias = "parport",
+            .parse = qemu_chr_parse_parallel,
+            .create = qmp_chardev_open_parallel,
+        },
 #endif
 #ifdef HAVE_CHARDEV_PTY
-    register_char_driver("pty", CHARDEV_BACKEND_KIND_PTY, NULL,
-                         qemu_chr_open_pty);
+        {
+            .kind = CHARDEV_BACKEND_KIND_PTY,
+            .create = qemu_chr_open_pty,
+        },
 #endif
 #ifdef _WIN32
-    register_char_driver("console", CHARDEV_BACKEND_KIND_CONSOLE, NULL,
-                         qemu_chr_open_win_con);
+        {
+            .kind = CHARDEV_BACKEND_KIND_CONSOLE,
+            .create = qemu_chr_open_win_con,
+        },
 #endif
-    register_char_driver("pipe", CHARDEV_BACKEND_KIND_PIPE,
-                         qemu_chr_parse_pipe, qemu_chr_open_pipe);
-    register_char_driver("mux", CHARDEV_BACKEND_KIND_MUX, qemu_chr_parse_mux,
-                         qemu_chr_open_mux);
-    /* Bug-compatibility: */
-    register_char_driver("memory", CHARDEV_BACKEND_KIND_MEMORY,
-                         qemu_chr_parse_ringbuf, qemu_chr_open_ringbuf);
+        {
+            .kind = CHARDEV_BACKEND_KIND_PIPE,
+            .parse = qemu_chr_parse_pipe,
+            .create = qemu_chr_open_pipe,
+        },
+        {
+            .kind = CHARDEV_BACKEND_KIND_MUX,
+            .parse = qemu_chr_parse_mux,
+            .create = qemu_chr_open_mux,
+        },
+        /* Bug-compatibility: */
+        {
+            .kind = CHARDEV_BACKEND_KIND_MEMORY,
+            .parse = qemu_chr_parse_ringbuf,
+            .create = qemu_chr_open_ringbuf,
+        },
+    };
+
+
+    for (i = 0; i < ARRAY_SIZE(drivers); i++) {
+        register_char_driver(&drivers[i]);
+    }
+
     /* this must be done after machine init, since we register FEs with muxes
      * as part of realize functions like serial_isa_realizefn when -nographic
      * is specified
diff --git a/spice-qemu-char.c b/spice-qemu-char.c
index 276c4ae..30db420 100644
--- a/spice-qemu-char.c
+++ b/spice-qemu-char.c
@@ -389,10 +389,18 @@ static void qemu_chr_parse_spice_port(QemuOpts *opts, ChardevBackend *backend,
 
 static void register_types(void)
 {
-    register_char_driver("spicevmc", CHARDEV_BACKEND_KIND_SPICEVMC,
-                         qemu_chr_parse_spice_vmc, qemu_chr_open_spice_vmc);
-    register_char_driver("spiceport", CHARDEV_BACKEND_KIND_SPICEPORT,
-                         qemu_chr_parse_spice_port, qemu_chr_open_spice_port);
+    static const CharDriver vmc_driver = {
+        .kind = CHARDEV_BACKEND_KIND_SPICEVMC,
+        .parse = qemu_chr_parse_spice_vmc,
+        .create = qemu_chr_open_spice_vmc,
+    };
+    static const CharDriver port_driver = {
+        .kind = CHARDEV_BACKEND_KIND_SPICEPORT,
+        .parse = qemu_chr_parse_spice_port,
+        .create = qemu_chr_open_spice_port,
+    };
+    register_char_driver(&vmc_driver);
+    register_char_driver(&port_driver);
 }
 
 type_init(register_types);
diff --git a/ui/console.c b/ui/console.c
index b9575f2..e4bb22f 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -2191,12 +2191,16 @@ static const TypeInfo qemu_console_info = {
     .class_size = sizeof(QemuConsoleClass),
 };
 
-
 static void register_types(void)
 {
+    static const CharDriver vc_driver = {
+        .kind = CHARDEV_BACKEND_KIND_VC,
+        .parse = qemu_chr_parse_vc,
+        .create = vc_init,
+    };
+
     type_register_static(&qemu_console_info);
-    register_char_driver("vc", CHARDEV_BACKEND_KIND_VC, qemu_chr_parse_vc,
-                         vc_init);
+    register_char_driver(&vc_driver);
 }
 
 type_init(register_types);
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [Qemu-devel] [PULL 26/41] char: use a static array for backends
  2017-01-27 13:45 [Qemu-devel] [PULL 00/41] Misc changes for 2017-01-27 Paolo Bonzini
                   ` (24 preceding siblings ...)
  2017-01-27 13:45 ` [Qemu-devel] [PULL 25/41] char: use a const CharDriver Paolo Bonzini
@ 2017-01-27 13:45 ` Paolo Bonzini
  2017-01-27 13:45 ` [Qemu-devel] [PULL 27/41] char: move callbacks in CharDriver Paolo Bonzini
                   ` (15 subsequent siblings)
  41 siblings, 0 replies; 43+ messages in thread
From: Paolo Bonzini @ 2017-01-27 13:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Number and kinds of backends is known at compile-time, use a fixed-sized
static array to simplify iterations & lookups.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 qemu-char.c | 68 ++++++++++++++++++++++++++++++-------------------------------
 1 file changed, 34 insertions(+), 34 deletions(-)

diff --git a/qemu-char.c b/qemu-char.c
index 51558a7..464c69d 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -4101,21 +4101,20 @@ static void qemu_chr_parse_udp(QemuOpts *opts, ChardevBackend *backend,
     }
 }
 
-static GSList *backends;
+static const CharDriver *backends[CHARDEV_BACKEND_KIND__MAX];
 
 void register_char_driver(const CharDriver *driver)
 {
-    /* casting away const */
-    backends = g_slist_append(backends, (void *)driver);
+    backends[driver->kind] = driver;
 }
 
 CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
                                         Error **errp)
 {
     Error *local_err = NULL;
-    CharDriver *cd;
+    const CharDriver *cd = NULL;
     CharDriverState *chr;
-    GSList *i;
+    int i;
     ChardevReturn *ret = NULL;
     ChardevBackend *backend;
     const char *name = qemu_opt_get(opts, "backend");
@@ -4130,11 +4129,13 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
 
     if (is_help_option(name)) {
         fprintf(stderr, "Available chardev backend types:\n");
-        for (i = backends; i; i = i->next) {
-            cd = i->data;
-            fprintf(stderr, "%s\n", ChardevBackendKind_lookup[cd->kind]);
-            if (cd->alias) {
-                fprintf(stderr, "%s\n", cd->alias);
+        for (i = 0; i < ARRAY_SIZE(backends); i++) {
+            cd = backends[i];
+            if (cd) {
+                fprintf(stderr, "%s\n", ChardevBackendKind_lookup[cd->kind]);
+                if (cd->alias) {
+                    fprintf(stderr, "%s\n", cd->alias);
+                }
             }
         }
         exit(0);
@@ -4145,15 +4146,17 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
         goto err;
     }
 
-    for (i = backends; i; i = i->next) {
-        cd = i->data;
-
+    for (i = 0; i < ARRAY_SIZE(backends); i++) {
+        cd = backends[i];
+        if (!cd) {
+            continue;
+        }
         if (g_strcmp0(ChardevBackendKind_lookup[cd->kind], name) == 0 ||
             g_strcmp0(cd->alias, name) == 0) {
             break;
         }
     }
-    if (i == NULL) {
+    if (i == ARRAY_SIZE(backends)) {
         error_setg(errp, "chardev: backend \"%s\" not found", name);
         goto err;
     }
@@ -4370,11 +4373,15 @@ qmp_prepend_backend(ChardevBackendInfoList *list, const char *name)
 ChardevBackendInfoList *qmp_query_chardev_backends(Error **errp)
 {
     ChardevBackendInfoList *backend_list = NULL;
-    CharDriver *c;
-    GSList *i;
+    const CharDriver *c;
+    int i;
+
+    for (i = 0; i < ARRAY_SIZE(backends); i++) {
+        c = backends[i];
+        if (!c) {
+            continue;
+        }
 
-    for (i = backends; i; i = i->next) {
-        c = i->data;
         backend_list = qmp_prepend_backend(backend_list,
                                            ChardevBackendKind_lookup[c->kind]);
         if (c->alias) {
@@ -4835,9 +4842,8 @@ ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
 {
     ChardevReturn *ret = g_new0(ChardevReturn, 1);
     CharDriverState *chr = NULL;
+    const CharDriver *cd;
     Error *local_err = NULL;
-    GSList *i;
-    CharDriver *cd;
     bool be_opened = true;
 
     chr = qemu_chr_find(id);
@@ -4846,22 +4852,16 @@ ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
         goto out_error;
     }
 
-    for (i = backends; i; i = i->next) {
-        cd = i->data;
-
-        if (cd->kind == backend->type) {
-            chr = cd->create(id, backend, ret, &be_opened, &local_err);
-            if (local_err) {
-                error_propagate(errp, local_err);
-                goto out_error;
-            }
-            break;
-        }
+    cd = (int)backend->type >= 0 && backend->type < ARRAY_SIZE(backends) ?
+        backends[backend->type] : NULL;
+    if (cd == NULL) {
+        error_setg(errp, "chardev backend not available");
+        goto out_error;
     }
 
-    if (chr == NULL) {
-        assert(!i);
-        error_setg(errp, "chardev backend not available");
+    chr = cd->create(id, backend, ret, &be_opened, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
         goto out_error;
     }
 
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [Qemu-devel] [PULL 27/41] char: move callbacks in CharDriver
  2017-01-27 13:45 [Qemu-devel] [PULL 00/41] Misc changes for 2017-01-27 Paolo Bonzini
                   ` (25 preceding siblings ...)
  2017-01-27 13:45 ` [Qemu-devel] [PULL 26/41] char: use a static array for backends Paolo Bonzini
@ 2017-01-27 13:45 ` Paolo Bonzini
  2017-01-27 13:45 ` [Qemu-devel] [PULL 28/41] char: fold single-user functions in caller Paolo Bonzini
                   ` (14 subsequent siblings)
  41 siblings, 0 replies; 43+ messages in thread
From: Paolo Bonzini @ 2017-01-27 13:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau

From: Marc-André Lureau <marcandre.lureau@redhat.com>

This makes the code more declarative, and avoids duplicating the
information on all instances.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 backends/baum.c       |  11 +-
 backends/msmouse.c    |  11 +-
 backends/testdev.c    |   8 +-
 gdbstub.c             |   7 +-
 hw/bt/hci-csr.c       |   8 +-
 include/sysemu/char.h |  46 ++---
 qemu-char.c           | 479 ++++++++++++++++++++++++++++++--------------------
 spice-qemu-char.c     |  32 ++--
 ui/console.c          |  28 +--
 ui/gtk.c              |  11 +-
 10 files changed, 381 insertions(+), 260 deletions(-)

diff --git a/backends/baum.c b/backends/baum.c
index 2e404a1..109469e 100644
--- a/backends/baum.c
+++ b/backends/baum.c
@@ -622,7 +622,8 @@ static void baum_free(struct CharDriverState *chr)
     g_free(baum);
 }
 
-static CharDriverState *chr_baum_init(const char *id,
+static CharDriverState *chr_baum_init(const CharDriver *driver,
+                                      const char *id,
                                       ChardevBackend *backend,
                                       ChardevReturn *ret,
                                       bool *be_opened,
@@ -633,7 +634,7 @@ static CharDriverState *chr_baum_init(const char *id,
     CharDriverState *chr;
     brlapi_handle_t *handle;
 
-    chr = qemu_chr_alloc(common, errp);
+    chr = qemu_chr_alloc(driver, common, errp);
     if (!chr) {
         return NULL;
     }
@@ -641,9 +642,6 @@ static CharDriverState *chr_baum_init(const char *id,
     baum->chr = chr;
 
     chr->opaque = baum;
-    chr->chr_write = baum_write;
-    chr->chr_accept_input = baum_accept_input;
-    chr->chr_free = baum_free;
 
     handle = g_malloc0(brlapi_getHandleSize());
     baum->brlapi = handle;
@@ -674,6 +672,9 @@ static void register_types(void)
     static const CharDriver driver = {
         .kind = CHARDEV_BACKEND_KIND_BRAILLE,
         .create = chr_baum_init,
+        .chr_write = baum_write,
+        .chr_accept_input = baum_accept_input,
+        .chr_free = baum_free,
     };
 
     register_char_driver(&driver);
diff --git a/backends/msmouse.c b/backends/msmouse.c
index 2490b2c..2c238ba 100644
--- a/backends/msmouse.c
+++ b/backends/msmouse.c
@@ -148,7 +148,8 @@ static QemuInputHandler msmouse_handler = {
     .sync  = msmouse_input_sync,
 };
 
-static CharDriverState *qemu_chr_open_msmouse(const char *id,
+static CharDriverState *qemu_chr_open_msmouse(const CharDriver *driver,
+                                              const char *id,
                                               ChardevBackend *backend,
                                               ChardevReturn *ret,
                                               bool *be_opened,
@@ -158,13 +159,10 @@ static CharDriverState *qemu_chr_open_msmouse(const char *id,
     MouseState *mouse;
     CharDriverState *chr;
 
-    chr = qemu_chr_alloc(common, errp);
+    chr = qemu_chr_alloc(driver, common, errp);
     if (!chr) {
         return NULL;
     }
-    chr->chr_write = msmouse_chr_write;
-    chr->chr_free = msmouse_chr_free;
-    chr->chr_accept_input = msmouse_chr_accept_input;
     *be_opened = false;
 
     mouse = g_new0(MouseState, 1);
@@ -182,6 +180,9 @@ static void register_types(void)
     static const CharDriver driver = {
         .kind = CHARDEV_BACKEND_KIND_MSMOUSE,
         .create = qemu_chr_open_msmouse,
+        .chr_write = msmouse_chr_write,
+        .chr_accept_input = msmouse_chr_accept_input,
+        .chr_free = msmouse_chr_free,
     };
     register_char_driver(&driver);
 }
diff --git a/backends/testdev.c b/backends/testdev.c
index cd25094..2339693 100644
--- a/backends/testdev.c
+++ b/backends/testdev.c
@@ -109,7 +109,8 @@ static void testdev_free(struct CharDriverState *chr)
     g_free(testdev);
 }
 
-static CharDriverState *chr_testdev_init(const char *id,
+static CharDriverState *chr_testdev_init(const CharDriver *driver,
+                                         const char *id,
                                          ChardevBackend *backend,
                                          ChardevReturn *ret,
                                          bool *be_opened,
@@ -121,9 +122,8 @@ static CharDriverState *chr_testdev_init(const char *id,
     testdev = g_new0(TestdevCharState, 1);
     testdev->chr = chr = g_new0(CharDriverState, 1);
 
+    chr->driver = driver;
     chr->opaque = testdev;
-    chr->chr_write = testdev_write;
-    chr->chr_free = testdev_free;
 
     return chr;
 }
@@ -133,6 +133,8 @@ static void register_types(void)
     static const CharDriver driver = {
         .kind = CHARDEV_BACKEND_KIND_TESTDEV,
         .create = chr_testdev_init,
+        .chr_write = testdev_write,
+        .chr_free = testdev_free,
     };
     register_char_driver(&driver);
 }
diff --git a/gdbstub.c b/gdbstub.c
index de9b62b..3de9bcb 100644
--- a/gdbstub.c
+++ b/gdbstub.c
@@ -1731,6 +1731,10 @@ int gdbserver_start(const char *device)
     CharDriverState *chr = NULL;
     CharDriverState *mon_chr;
     ChardevCommon common = { 0 };
+    static const CharDriver driver = {
+        .kind = -1,
+        .chr_write = gdb_monitor_write,
+    };
 
     if (!device)
         return -1;
@@ -1763,8 +1767,7 @@ int gdbserver_start(const char *device)
         qemu_add_vm_change_state_handler(gdb_vm_state_change, NULL);
 
         /* Initialize a monitor terminal for gdb */
-        mon_chr = qemu_chr_alloc(&common, &error_abort);
-        mon_chr->chr_write = gdb_monitor_write;
+        mon_chr = qemu_chr_alloc(&driver, &common, &error_abort);
         monitor_init(mon_chr, 0);
     } else {
         if (qemu_chr_fe_get_driver(&s->chr)) {
diff --git a/hw/bt/hci-csr.c b/hw/bt/hci-csr.c
index fbb3109..9c3fb3c 100644
--- a/hw/bt/hci-csr.c
+++ b/hw/bt/hci-csr.c
@@ -462,12 +462,16 @@ qemu_irq *csrhci_pins_get(CharDriverState *chr)
 
 CharDriverState *uart_hci_init(void)
 {
+    static const CharDriver hci_driver = {
+        .kind = -1,
+        .chr_write = csrhci_write,
+        .chr_ioctl = csrhci_ioctl,
+    };
     struct csrhci_s *s = (struct csrhci_s *)
             g_malloc0(sizeof(struct csrhci_s));
 
     s->chr.opaque = s;
-    s->chr.chr_write = csrhci_write;
-    s->chr.chr_ioctl = csrhci_ioctl;
+    s->chr.driver = &hci_driver;
 
     s->hci = qemu_next_hci();
     s->hci->opaque = s;
diff --git a/include/sysemu/char.h b/include/sysemu/char.h
index fee2c34..2dd0564 100644
--- a/include/sysemu/char.h
+++ b/include/sysemu/char.h
@@ -85,24 +85,11 @@ typedef struct CharBackend {
     int fe_open;
 } CharBackend;
 
+typedef struct CharDriver CharDriver;
+
 struct CharDriverState {
+    const CharDriver *driver;
     QemuMutex chr_write_lock;
-    int (*chr_write)(struct CharDriverState *s, const uint8_t *buf, int len);
-    int (*chr_sync_read)(struct CharDriverState *s,
-                         const uint8_t *buf, int len);
-    GSource *(*chr_add_watch)(struct CharDriverState *s, GIOCondition cond);
-    void (*chr_update_read_handler)(struct CharDriverState *s,
-                                    GMainContext *context);
-    int (*chr_ioctl)(struct CharDriverState *s, int cmd, void *arg);
-    int (*get_msgfds)(struct CharDriverState *s, int* fds, int num);
-    int (*set_msgfds)(struct CharDriverState *s, int *fds, int num);
-    int (*chr_add_client)(struct CharDriverState *chr, int fd);
-    int (*chr_wait_connected)(struct CharDriverState *chr, Error **errp);
-    void (*chr_free)(struct CharDriverState *chr);
-    void (*chr_disconnect)(struct CharDriverState *chr);
-    void (*chr_accept_input)(struct CharDriverState *chr);
-    void (*chr_set_echo)(struct CharDriverState *chr, bool echo);
-    void (*chr_set_fe_open)(struct CharDriverState *chr, int fe_open);
     CharBackend *be;
     void *opaque;
     char *label;
@@ -125,7 +112,8 @@ struct CharDriverState {
  *
  * Returns: a newly allocated CharDriverState, or NULL on error.
  */
-CharDriverState *qemu_chr_alloc(ChardevCommon *backend, Error **errp);
+CharDriverState *qemu_chr_alloc(const CharDriver *driver,
+                                ChardevCommon *backend, Error **errp);
 
 /**
  * @qemu_chr_new_from_opts:
@@ -475,15 +463,33 @@ void qemu_chr_set_feature(CharDriverState *chr,
                           CharDriverFeature feature);
 QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename);
 
-typedef struct CharDriver {
+struct CharDriver {
     ChardevBackendKind kind;
     const char *alias;
     void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp);
-    CharDriverState *(*create)(const char *id,
+    CharDriverState *(*create)(const CharDriver *driver,
+                               const char *id,
                                ChardevBackend *backend,
                                ChardevReturn *ret, bool *be_opened,
                                Error **errp);
-} CharDriver;
+
+    int (*chr_write)(struct CharDriverState *s, const uint8_t *buf, int len);
+    int (*chr_sync_read)(struct CharDriverState *s,
+                         const uint8_t *buf, int len);
+    GSource *(*chr_add_watch)(struct CharDriverState *s, GIOCondition cond);
+    void (*chr_update_read_handler)(struct CharDriverState *s,
+                                    GMainContext *context);
+    int (*chr_ioctl)(struct CharDriverState *s, int cmd, void *arg);
+    int (*get_msgfds)(struct CharDriverState *s, int* fds, int num);
+    int (*set_msgfds)(struct CharDriverState *s, int *fds, int num);
+    int (*chr_add_client)(struct CharDriverState *chr, int fd);
+    int (*chr_wait_connected)(struct CharDriverState *chr, Error **errp);
+    void (*chr_free)(struct CharDriverState *chr);
+    void (*chr_disconnect)(struct CharDriverState *chr);
+    void (*chr_accept_input)(struct CharDriverState *chr);
+    void (*chr_set_echo)(struct CharDriverState *chr, bool echo);
+    void (*chr_set_fe_open)(struct CharDriverState *chr, int fe_open);
+};
 
 void register_char_driver(const CharDriver *driver);
 
diff --git a/qemu-char.c b/qemu-char.c
index 464c69d..ce8d4bb 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -162,11 +162,15 @@ static QTAILQ_HEAD(CharDriverStateHead, CharDriverState) chardevs =
 
 static void qemu_chr_free_common(CharDriverState *chr);
 
-CharDriverState *qemu_chr_alloc(ChardevCommon *backend, Error **errp)
+CharDriverState *qemu_chr_alloc(const CharDriver *driver,
+                                ChardevCommon *backend, Error **errp)
 {
     CharDriverState *chr = g_malloc0(sizeof(CharDriverState));
     qemu_mutex_init(&chr->chr_write_lock);
 
+    assert(driver);
+    assert(driver->chr_write);
+
     if (backend->has_logfile) {
         int flags = O_WRONLY | O_CREAT;
         if (backend->has_logappend &&
@@ -186,6 +190,7 @@ CharDriverState *qemu_chr_alloc(ChardevCommon *backend, Error **errp)
     } else {
         chr->logfd = -1;
     }
+    chr->driver = driver;
 
     return chr;
 }
@@ -252,7 +257,7 @@ static int qemu_chr_fe_write_buffer(CharDriverState *s, const uint8_t *buf, int
     qemu_mutex_lock(&s->chr_write_lock);
     while (*offset < len) {
     retry:
-        res = s->chr_write(s, buf + *offset, len - *offset);
+        res = s->driver->chr_write(s, buf + *offset, len - *offset);
         if (res < 0 && errno == EAGAIN) {
             g_usleep(100);
             goto retry;
@@ -290,7 +295,7 @@ int qemu_chr_fe_write(CharBackend *be, const uint8_t *buf, int len)
     }
 
     qemu_mutex_lock(&s->chr_write_lock);
-    ret = s->chr_write(s, buf, len);
+    ret = s->driver->chr_write(s, buf, len);
 
     if (ret > 0) {
         qemu_chr_fe_write_log(s, buf, ret);
@@ -346,7 +351,7 @@ int qemu_chr_fe_read_all(CharBackend *be, uint8_t *buf, int len)
     int offset = 0, counter = 10;
     int res;
 
-    if (!s || !s->chr_sync_read) {
+    if (!s || !s->driver->chr_sync_read) {
         return 0;
     }
 
@@ -356,7 +361,7 @@ int qemu_chr_fe_read_all(CharBackend *be, uint8_t *buf, int len)
 
     while (offset < len) {
     retry:
-        res = s->chr_sync_read(s, buf + offset, len - offset);
+        res = s->driver->chr_sync_read(s, buf + offset, len - offset);
         if (res == -1 && errno == EAGAIN) {
             g_usleep(100);
             goto retry;
@@ -391,10 +396,10 @@ int qemu_chr_fe_ioctl(CharBackend *be, int cmd, void *arg)
     CharDriverState *s = be->chr;
     int res;
 
-    if (!s || !s->chr_ioctl || s->replay) {
+    if (!s || !s->driver->chr_ioctl || s->replay) {
         res = -ENOTSUP;
     } else {
-        res = s->chr_ioctl(s, cmd, arg);
+        res = s->driver->chr_ioctl(s, cmd, arg);
     }
 
     return res;
@@ -453,7 +458,7 @@ int qemu_chr_fe_get_msgfds(CharBackend *be, int *fds, int len)
         return -1;
     }
 
-    return s->get_msgfds ? s->get_msgfds(s, fds, len) : -1;
+    return s->driver->get_msgfds ? s->driver->get_msgfds(s, fds, len) : -1;
 }
 
 int qemu_chr_fe_set_msgfds(CharBackend *be, int *fds, int num)
@@ -464,12 +469,12 @@ int qemu_chr_fe_set_msgfds(CharBackend *be, int *fds, int num)
         return -1;
     }
 
-    return s->set_msgfds ? s->set_msgfds(s, fds, num) : -1;
+    return s->driver->set_msgfds ? s->driver->set_msgfds(s, fds, num) : -1;
 }
 
 int qemu_chr_add_client(CharDriverState *s, int fd)
 {
-    return s->chr_add_client ? s->chr_add_client(s, fd) : -1;
+    return s->driver->chr_add_client ? s->driver->chr_add_client(s, fd) : -1;
 }
 
 void qemu_chr_fe_accept_input(CharBackend *be)
@@ -480,8 +485,9 @@ void qemu_chr_fe_accept_input(CharBackend *be)
         return;
     }
 
-    if (s->chr_accept_input)
-        s->chr_accept_input(s);
+    if (s->driver->chr_accept_input) {
+        s->driver->chr_accept_input(s);
+    }
     qemu_notify_event();
 }
 
@@ -506,7 +512,8 @@ static int null_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
     return len;
 }
 
-static CharDriverState *qemu_chr_open_null(const char *id,
+static CharDriverState *qemu_chr_open_null(const CharDriver *driver,
+                                           const char *id,
                                            ChardevBackend *backend,
                                            ChardevReturn *ret,
                                            bool *be_opened,
@@ -515,15 +522,20 @@ static CharDriverState *qemu_chr_open_null(const char *id,
     CharDriverState *chr;
     ChardevCommon *common = backend->u.null.data;
 
-    chr = qemu_chr_alloc(common, errp);
+    chr = qemu_chr_alloc(driver, common, errp);
     if (!chr) {
         return NULL;
     }
-    chr->chr_write = null_chr_write;
     *be_opened = false;
     return chr;
 }
 
+static const CharDriver null_driver = {
+    .kind = CHARDEV_BACKEND_KIND_NULL,
+    .create = qemu_chr_open_null,
+    .chr_write = null_chr_write,
+};
+
 /* MUX driver for serial I/O splitting */
 #define MAX_MUX 4
 #define MUX_BUFFER_SIZE 32	/* Must be a power of 2.  */
@@ -795,7 +807,11 @@ static GSource *mux_chr_add_watch(CharDriverState *s, GIOCondition cond)
     MuxDriver *d = s->opaque;
     CharDriverState *chr = qemu_chr_fe_get_driver(&d->chr);
 
-    return chr->chr_add_watch(chr, cond);
+    if (!chr->driver->chr_add_watch) {
+        return NULL;
+    }
+
+    return chr->driver->chr_add_watch(chr, cond);
 }
 
 static void mux_chr_free(struct CharDriverState *chr)
@@ -842,7 +858,8 @@ static void mux_set_focus(CharDriverState *chr, int focus)
     mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_IN);
 }
 
-static CharDriverState *qemu_chr_open_mux(const char *id,
+static CharDriverState *qemu_chr_open_mux(const CharDriver *driver,
+                                          const char *id,
                                           ChardevBackend *backend,
                                           ChardevReturn *ret,
                                           bool *be_opened,
@@ -859,7 +876,7 @@ static CharDriverState *qemu_chr_open_mux(const char *id,
         return NULL;
     }
 
-    chr = qemu_chr_alloc(common, errp);
+    chr = qemu_chr_alloc(driver, common, errp);
     if (!chr) {
         return NULL;
     }
@@ -867,14 +884,6 @@ static CharDriverState *qemu_chr_open_mux(const char *id,
 
     chr->opaque = d;
     d->focus = -1;
-    chr->chr_free = mux_chr_free;
-    chr->chr_write = mux_chr_write;
-    chr->chr_accept_input = mux_chr_accept_input;
-    /* Frontend guest-open / -close notification is not support with muxes */
-    chr->chr_set_fe_open = NULL;
-    if (drv->chr_add_watch) {
-        chr->chr_add_watch = mux_chr_add_watch;
-    }
     /* only default to opened state if we've realized the initial
      * set of muxes
      */
@@ -975,8 +984,8 @@ void qemu_chr_fe_set_handlers(CharBackend *b,
     b->chr_read = fd_read;
     b->chr_event = fd_event;
     b->opaque = opaque;
-    if (s->chr_update_read_handler) {
-        s->chr_update_read_handler(s, context);
+    if (s->driver->chr_update_read_handler) {
+        s->driver->chr_update_read_handler(s, context);
     }
 
     if (set_open) {
@@ -1271,14 +1280,15 @@ static void fd_chr_free(struct CharDriverState *chr)
 }
 
 /* open a character device to a unix fd */
-static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out,
+static CharDriverState *qemu_chr_open_fd(const CharDriver *driver,
+                                         int fd_in, int fd_out,
                                          ChardevCommon *backend, Error **errp)
 {
     CharDriverState *chr;
     FDCharDriver *s;
     char *name;
 
-    chr = qemu_chr_alloc(backend, errp);
+    chr = qemu_chr_alloc(driver, backend, errp);
     if (!chr) {
         return NULL;
     }
@@ -1294,15 +1304,12 @@ static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out,
     qemu_set_nonblock(fd_out);
     s->chr = chr;
     chr->opaque = s;
-    chr->chr_add_watch = fd_chr_add_watch;
-    chr->chr_write = fd_chr_write;
-    chr->chr_update_read_handler = fd_chr_update_read_handler;
-    chr->chr_free = fd_chr_free;
 
     return chr;
 }
 
-static CharDriverState *qemu_chr_open_pipe(const char *id,
+static CharDriverState *qemu_chr_open_pipe(const CharDriver *driver,
+                                           const char *id,
                                            ChardevBackend *backend,
                                            ChardevReturn *ret,
                                            bool *be_opened,
@@ -1333,7 +1340,7 @@ static CharDriverState *qemu_chr_open_pipe(const char *id,
             return NULL;
         }
     }
-    return qemu_chr_open_fd(fd_in, fd_out, common, errp);
+    return qemu_chr_open_fd(driver, fd_in, fd_out, common, errp);
 }
 
 /* init terminal so that we can grab keys */
@@ -1385,7 +1392,8 @@ static void qemu_chr_free_stdio(struct CharDriverState *chr)
     fd_chr_free(chr);
 }
 
-static CharDriverState *qemu_chr_open_stdio(const char *id,
+static CharDriverState *qemu_chr_open_stdio(const CharDriver *driver,
+                                            const char *id,
                                             ChardevBackend *backend,
                                             ChardevReturn *ret,
                                             bool *be_opened,
@@ -1416,12 +1424,10 @@ static CharDriverState *qemu_chr_open_stdio(const char *id,
     act.sa_handler = term_stdio_handler;
     sigaction(SIGCONT, &act, NULL);
 
-    chr = qemu_chr_open_fd(0, 1, common, errp);
+    chr = qemu_chr_open_fd(driver, 0, 1, common, errp);
     if (!chr) {
         return NULL;
     }
-    chr->chr_free = qemu_chr_free_stdio;
-    chr->chr_set_echo = qemu_chr_set_echo_stdio;
     if (opts->has_signal) {
         stdio_allow_signal = opts->signal;
     }
@@ -1638,7 +1644,8 @@ static void pty_chr_free(struct CharDriverState *chr)
     qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
 }
 
-static CharDriverState *qemu_chr_open_pty(const char *id,
+static CharDriverState *qemu_chr_open_pty(const CharDriver *driver,
+                                          const char *id,
                                           ChardevBackend *backend,
                                           ChardevReturn *ret,
                                           bool *be_opened,
@@ -1660,7 +1667,7 @@ static CharDriverState *qemu_chr_open_pty(const char *id,
     close(slave_fd);
     qemu_set_nonblock(master_fd);
 
-    chr = qemu_chr_alloc(common, errp);
+    chr = qemu_chr_alloc(driver, common, errp);
     if (!chr) {
         close(master_fd);
         return NULL;
@@ -1675,10 +1682,6 @@ static CharDriverState *qemu_chr_open_pty(const char *id,
 
     s = g_new0(PtyCharDriver, 1);
     chr->opaque = s;
-    chr->chr_write = pty_chr_write;
-    chr->chr_update_read_handler = pty_chr_update_read_handler;
-    chr->chr_free = pty_chr_free;
-    chr->chr_add_watch = pty_chr_add_watch;
     *be_opened = false;
 
     s->ioc = QIO_CHANNEL(qio_channel_file_new_fd(master_fd));
@@ -1690,6 +1693,15 @@ static CharDriverState *qemu_chr_open_pty(const char *id,
     return chr;
 }
 
+static const CharDriver pty_driver = {
+    .kind = CHARDEV_BACKEND_KIND_PTY,
+    .create = qemu_chr_open_pty,
+    .chr_write = pty_chr_write,
+    .chr_update_read_handler = pty_chr_update_read_handler,
+    .chr_add_watch = pty_chr_add_watch,
+    .chr_free = pty_chr_free,
+};
+
 static void tty_serial_init(int fd, int speed,
                             int parity, int data_bits, int stop_bits)
 {
@@ -1880,7 +1892,8 @@ static void qemu_chr_free_tty(CharDriverState *chr)
     fd_chr_free(chr);
 }
 
-static CharDriverState *qemu_chr_open_tty_fd(int fd,
+static CharDriverState *qemu_chr_open_tty_fd(const CharDriver *driver,
+                                             int fd,
                                              ChardevCommon *backend,
                                              bool *be_opened,
                                              Error **errp)
@@ -1888,12 +1901,10 @@ static CharDriverState *qemu_chr_open_tty_fd(int fd,
     CharDriverState *chr;
 
     tty_serial_init(fd, 115200, 'N', 8, 1);
-    chr = qemu_chr_open_fd(fd, fd, backend, errp);
+    chr = qemu_chr_open_fd(driver, fd, fd, backend, errp);
     if (!chr) {
         return NULL;
     }
-    chr->chr_ioctl = tty_serial_ioctl;
-    chr->chr_free = qemu_chr_free_tty;
     return chr;
 }
 #endif /* __linux__ || __sun__ */
@@ -2011,7 +2022,8 @@ static void pp_free(CharDriverState *chr)
     qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
 }
 
-static CharDriverState *qemu_chr_open_pp_fd(int fd,
+static CharDriverState *qemu_chr_open_pp_fd(const CharDriver *driver,
+                                            int fd,
                                             ChardevCommon *backend,
                                             bool *be_opened,
                                             Error **errp)
@@ -2025,16 +2037,13 @@ static CharDriverState *qemu_chr_open_pp_fd(int fd,
         return NULL;
     }
 
-    chr = qemu_chr_alloc(backend, errp);
+    chr = qemu_chr_alloc(driver, backend, errp);
     if (!chr) {
         return NULL;
     }
 
     drv = g_new0(ParallelCharDriver, 1);
     chr->opaque = drv;
-    chr->chr_write = null_chr_write;
-    chr->chr_ioctl = pp_ioctl;
-    chr->chr_free = pp_free;
 
     drv->fd = fd;
     drv->mode = IEEE1284_MODE_COMPAT;
@@ -2084,20 +2093,19 @@ static int pp_ioctl(CharDriverState *chr, int cmd, void *arg)
     return 0;
 }
 
-static CharDriverState *qemu_chr_open_pp_fd(int fd,
+static CharDriverState *qemu_chr_open_pp_fd(const CharDriver *driver,
+                                            int fd,
                                             ChardevCommon *backend,
                                             bool *be_opened,
                                             Error **errp)
 {
     CharDriverState *chr;
 
-    chr = qemu_chr_alloc(backend, errp);
+    chr = qemu_chr_alloc(driver, backend, errp);
     if (!chr) {
         return NULL;
     }
     chr->opaque = (void *)(intptr_t)fd;
-    chr->chr_write = null_chr_write;
-    chr->chr_ioctl = pp_ioctl;
     *be_opened = false;
     return chr;
 }
@@ -2319,21 +2327,20 @@ static int win_chr_poll(void *opaque)
     return 0;
 }
 
-static CharDriverState *qemu_chr_open_win_path(const char *filename,
+static CharDriverState *qemu_chr_open_win_path(const CharDriver *driver,
+                                               const char *filename,
                                                ChardevCommon *backend,
                                                Error **errp)
 {
     CharDriverState *chr;
     WinCharState *s;
 
-    chr = qemu_chr_alloc(backend, errp);
+    chr = qemu_chr_alloc(driver, backend, errp);
     if (!chr) {
         return NULL;
     }
     s = g_new0(WinCharState, 1);
     chr->opaque = s;
-    chr->chr_write = win_chr_write;
-    chr->chr_free = win_chr_free;
 
     if (win_chr_init(chr, filename, errp) < 0) {
         g_free(s);
@@ -2424,7 +2431,8 @@ static int win_chr_pipe_init(CharDriverState *chr, const char *filename,
 }
 
 
-static CharDriverState *qemu_chr_open_pipe(const char *id,
+static CharDriverState *qemu_chr_open_pipe(const CharDriver *driver,
+                                           const char *id,
                                            ChardevBackend *backend,
                                            ChardevReturn *ret,
                                            bool *be_opened,
@@ -2436,14 +2444,12 @@ static CharDriverState *qemu_chr_open_pipe(const char *id,
     WinCharState *s;
     ChardevCommon *common = qapi_ChardevHostdev_base(opts);
 
-    chr = qemu_chr_alloc(common, errp);
+    chr = qemu_chr_alloc(driver, common, errp);
     if (!chr) {
         return NULL;
     }
     s = g_new0(WinCharState, 1);
     chr->opaque = s;
-    chr->chr_write = win_chr_write;
-    chr->chr_free = win_chr_free;
 
     if (win_chr_pipe_init(chr, filename, errp) < 0) {
         g_free(s);
@@ -2453,35 +2459,43 @@ static CharDriverState *qemu_chr_open_pipe(const char *id,
     return chr;
 }
 
-static CharDriverState *qemu_chr_open_win_file(HANDLE fd_out,
+static CharDriverState *qemu_chr_open_win_file(const CharDriver *driver,
+                                               HANDLE fd_out,
                                                ChardevCommon *backend,
                                                Error **errp)
 {
     CharDriverState *chr;
     WinCharState *s;
 
-    chr = qemu_chr_alloc(backend, errp);
+    chr = qemu_chr_alloc(driver, backend, errp);
     if (!chr) {
         return NULL;
     }
     s = g_new0(WinCharState, 1);
     s->hcom = fd_out;
     chr->opaque = s;
-    chr->chr_write = win_chr_write;
     return chr;
 }
 
-static CharDriverState *qemu_chr_open_win_con(const char *id,
+static CharDriverState *qemu_chr_open_win_con(const CharDriver *driver,
+                                              const char *id,
                                               ChardevBackend *backend,
                                               ChardevReturn *ret,
                                               bool *be_opened,
                                               Error **errp)
 {
     ChardevCommon *common = backend->u.console.data;
-    return qemu_chr_open_win_file(GetStdHandle(STD_OUTPUT_HANDLE),
+    return qemu_chr_open_win_file(driver,
+                                  GetStdHandle(STD_OUTPUT_HANDLE),
                                   common, errp);
 }
 
+static const CharDriver console_driver = {
+    .kind = CHARDEV_BACKEND_KIND_CONSOLE,
+    .create = qemu_chr_open_win_con,
+    .chr_write = win_chr_write,
+};
+
 static int win_stdio_write(CharDriverState *chr, const uint8_t *buf, int len)
 {
     HANDLE  hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
@@ -2617,7 +2631,8 @@ static void win_stdio_free(CharDriverState *chr)
     g_free(chr->opaque);
 }
 
-static CharDriverState *qemu_chr_open_stdio(const char *id,
+static CharDriverState *qemu_chr_open_stdio(const CharDriver *driver,
+                                            const char *id,
                                             ChardevBackend *backend,
                                             ChardevReturn *ret,
                                             bool *be_opened,
@@ -2629,7 +2644,7 @@ static CharDriverState *qemu_chr_open_stdio(const char *id,
     int                is_console = 0;
     ChardevCommon *common = qapi_ChardevStdio_base(backend->u.stdio.data);
 
-    chr   = qemu_chr_alloc(common, errp);
+    chr = qemu_chr_alloc(driver, common, errp);
     if (!chr) {
         return NULL;
     }
@@ -2644,8 +2659,6 @@ static CharDriverState *qemu_chr_open_stdio(const char *id,
     is_console = GetConsoleMode(stdio->hStdIn, &dwMode) != 0;
 
     chr->opaque    = stdio;
-    chr->chr_write = win_stdio_write;
-    chr->chr_free = win_stdio_free;
 
     if (is_console) {
         if (qemu_add_wait_object(stdio->hStdIn,
@@ -2687,7 +2700,6 @@ static CharDriverState *qemu_chr_open_stdio(const char *id,
 
     SetConsoleMode(stdio->hStdIn, dwMode);
 
-    chr->chr_set_echo = qemu_chr_set_echo_win_stdio;
     qemu_chr_set_echo_win_stdio(chr, false);
 
     return chr;
@@ -2794,7 +2806,8 @@ static void udp_chr_free(CharDriverState *chr)
     qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
 }
 
-static CharDriverState *qemu_chr_open_udp(QIOChannelSocket *sioc,
+static CharDriverState *qemu_chr_open_udp(const CharDriver *driver,
+                                          QIOChannelSocket *sioc,
                                           ChardevCommon *backend,
                                           bool *be_opened,
                                           Error **errp)
@@ -2802,7 +2815,7 @@ static CharDriverState *qemu_chr_open_udp(QIOChannelSocket *sioc,
     CharDriverState *chr = NULL;
     NetCharDriver *s = NULL;
 
-    chr = qemu_chr_alloc(backend, errp);
+    chr = qemu_chr_alloc(driver, backend, errp);
     if (!chr) {
         return NULL;
     }
@@ -2812,9 +2825,6 @@ static CharDriverState *qemu_chr_open_udp(QIOChannelSocket *sioc,
     s->bufcnt = 0;
     s->bufptr = 0;
     chr->opaque = s;
-    chr->chr_write = udp_chr_write;
-    chr->chr_update_read_handler = udp_chr_update_read_handler;
-    chr->chr_free = udp_chr_free;
     /* be isn't opened until we get a connection */
     *be_opened = false;
     return chr;
@@ -3448,8 +3458,8 @@ static int tcp_chr_wait_connected(CharDriverState *chr, Error **errp)
 
 static int qemu_chr_wait_connected(CharDriverState *chr, Error **errp)
 {
-    if (chr->chr_wait_connected) {
-        return chr->chr_wait_connected(chr, errp);
+    if (chr->driver->chr_wait_connected) {
+        return chr->driver->chr_wait_connected(chr, errp);
     }
 
     return 0;
@@ -3572,7 +3582,8 @@ static void ringbuf_chr_free(struct CharDriverState *chr)
     chr->opaque = NULL;
 }
 
-static CharDriverState *qemu_chr_open_ringbuf(const char *id,
+static CharDriverState *qemu_chr_open_ringbuf(const CharDriver *driver,
+                                              const char *id,
                                               ChardevBackend *backend,
                                               ChardevReturn *ret,
                                               bool *be_opened,
@@ -3583,7 +3594,7 @@ static CharDriverState *qemu_chr_open_ringbuf(const char *id,
     CharDriverState *chr;
     RingBufCharDriver *d;
 
-    chr = qemu_chr_alloc(common, errp);
+    chr = qemu_chr_alloc(driver, common, errp);
     if (!chr) {
         return NULL;
     }
@@ -3602,8 +3613,6 @@ static CharDriverState *qemu_chr_open_ringbuf(const char *id,
     d->cbuf = g_malloc0(d->size);
 
     chr->opaque = d;
-    chr->chr_write = ringbuf_chr_write;
-    chr->chr_free = ringbuf_chr_free;
 
     return chr;
 
@@ -3615,7 +3624,7 @@ fail:
 
 bool chr_is_ringbuf(const CharDriverState *chr)
 {
-    return chr->chr_write == ringbuf_chr_write;
+    return chr->driver->chr_write == ringbuf_chr_write;
 }
 
 void qmp_ringbuf_write(const char *device, const char *data,
@@ -3894,6 +3903,23 @@ static void qemu_chr_parse_stdio(QemuOpts *opts, ChardevBackend *backend,
     stdio->signal = qemu_opt_get_bool(opts, "signal", true);
 }
 
+static const CharDriver stdio_driver = {
+    .kind = CHARDEV_BACKEND_KIND_STDIO,
+    .parse = qemu_chr_parse_stdio,
+    .create = qemu_chr_open_stdio,
+#ifdef _WIN32
+    .chr_write = win_stdio_write,
+    .chr_set_echo = qemu_chr_set_echo_win_stdio,
+    .chr_free = win_stdio_free,
+#else
+    .chr_add_watch = fd_chr_add_watch,
+    .chr_write = fd_chr_write,
+    .chr_update_read_handler = fd_chr_update_read_handler,
+    .chr_set_echo = qemu_chr_set_echo_stdio,
+    .chr_free = qemu_chr_free_stdio,
+#endif
+};
+
 #ifdef HAVE_CHARDEV_SERIAL
 static void qemu_chr_parse_serial(QemuOpts *opts, ChardevBackend *backend,
                                   Error **errp)
@@ -3943,6 +3969,21 @@ static void qemu_chr_parse_pipe(QemuOpts *opts, ChardevBackend *backend,
     dev->device = g_strdup(device);
 }
 
+static const CharDriver pipe_driver = {
+    .kind = CHARDEV_BACKEND_KIND_PIPE,
+    .parse = qemu_chr_parse_pipe,
+    .create = qemu_chr_open_pipe,
+#ifdef _WIN32
+    .chr_write = win_chr_write,
+    .chr_free = win_chr_free,
+#else
+    .chr_add_watch = fd_chr_add_watch,
+    .chr_write = fd_chr_write,
+    .chr_update_read_handler = fd_chr_update_read_handler,
+    .chr_free = fd_chr_free,
+#endif
+};
+
 static void qemu_chr_parse_ringbuf(QemuOpts *opts, ChardevBackend *backend,
                                    Error **errp)
 {
@@ -3959,6 +4000,23 @@ static void qemu_chr_parse_ringbuf(QemuOpts *opts, ChardevBackend *backend,
     }
 }
 
+static const CharDriver ringbuf_driver = {
+    .kind = CHARDEV_BACKEND_KIND_RINGBUF,
+    .parse = qemu_chr_parse_ringbuf,
+    .create = qemu_chr_open_ringbuf,
+    .chr_write = ringbuf_chr_write,
+    .chr_free = ringbuf_chr_free,
+};
+
+/* Bug-compatibility: */
+static const CharDriver memory_driver = {
+    .kind = CHARDEV_BACKEND_KIND_MEMORY,
+    .parse = qemu_chr_parse_ringbuf,
+    .create = qemu_chr_open_ringbuf,
+    .chr_write = ringbuf_chr_write,
+    .chr_free = ringbuf_chr_free,
+};
+
 static void qemu_chr_parse_mux(QemuOpts *opts, ChardevBackend *backend,
                                Error **errp)
 {
@@ -3974,6 +4032,16 @@ static void qemu_chr_parse_mux(QemuOpts *opts, ChardevBackend *backend,
     mux->chardev = g_strdup(chardev);
 }
 
+static const CharDriver mux_driver = {
+    .kind = CHARDEV_BACKEND_KIND_MUX,
+    .parse = qemu_chr_parse_mux,
+    .create = qemu_chr_open_mux,
+    .chr_free = mux_chr_free,
+    .chr_write = mux_chr_write,
+    .chr_accept_input = mux_chr_accept_input,
+    .chr_add_watch = mux_chr_add_watch,
+};
+
 static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
                                   Error **errp)
 {
@@ -4246,7 +4314,7 @@ CharDriverState *qemu_chr_new(const char *label, const char *filename)
     chr = qemu_chr_new_noreplay(label, filename);
     if (chr) {
         chr->replay = replay_mode != REPLAY_MODE_NONE;
-        if (chr->replay && chr->chr_ioctl) {
+        if (chr->replay && chr->driver->chr_ioctl) {
             fprintf(stderr,
                     "Replay: ioctl is not supported for serial devices yet\n");
         }
@@ -4259,8 +4327,8 @@ void qemu_chr_fe_set_echo(CharBackend *be, bool echo)
 {
     CharDriverState *chr = be->chr;
 
-    if (chr && chr->chr_set_echo) {
-        chr->chr_set_echo(chr, echo);
+    if (chr && chr->driver->chr_set_echo) {
+        chr->driver->chr_set_echo(chr, echo);
     }
 }
 
@@ -4276,8 +4344,8 @@ void qemu_chr_fe_set_open(CharBackend *be, int fe_open)
         return;
     }
     be->fe_open = fe_open;
-    if (chr->chr_set_fe_open) {
-        chr->chr_set_fe_open(chr, fe_open);
+    if (chr->driver->chr_set_fe_open) {
+        chr->driver->chr_set_fe_open(chr, fe_open);
     }
 }
 
@@ -4288,11 +4356,11 @@ guint qemu_chr_fe_add_watch(CharBackend *be, GIOCondition cond,
     GSource *src;
     guint tag;
 
-    if (!s || s->chr_add_watch == NULL) {
+    if (!s || s->driver->chr_add_watch == NULL) {
         return 0;
     }
 
-    src = s->chr_add_watch(s, cond);
+    src = s->driver->chr_add_watch(s, cond);
     if (!src) {
         return 0;
     }
@@ -4308,8 +4376,8 @@ void qemu_chr_fe_disconnect(CharBackend *be)
 {
     CharDriverState *chr = be->chr;
 
-    if (chr && chr->chr_disconnect) {
-        chr->chr_disconnect(chr);
+    if (chr && chr->driver->chr_disconnect) {
+        chr->driver->chr_disconnect(chr);
     }
 }
 
@@ -4329,8 +4397,8 @@ static void qemu_chr_free_common(CharDriverState *chr)
 
 void qemu_chr_free(CharDriverState *chr)
 {
-    if (chr->chr_free) {
-        chr->chr_free(chr);
+    if (chr->driver->chr_free) {
+        chr->driver->chr_free(chr);
     }
     qemu_chr_free_common(chr);
 }
@@ -4500,7 +4568,8 @@ QemuOptsList qemu_chardev_opts = {
 
 #ifdef _WIN32
 
-static CharDriverState *qmp_chardev_open_file(const char *id,
+static CharDriverState *qmp_chardev_open_file(const CharDriver *driver,
+                                              const char *id,
                                               ChardevBackend *backend,
                                               ChardevReturn *ret,
                                               bool *be_opened,
@@ -4533,10 +4602,11 @@ static CharDriverState *qmp_chardev_open_file(const char *id,
         error_setg(errp, "open %s failed", file->out);
         return NULL;
     }
-    return qemu_chr_open_win_file(out, common, errp);
+    return qemu_chr_open_win_file(driver, out, common, errp);
 }
 
-static CharDriverState *qmp_chardev_open_serial(const char *id,
+static CharDriverState *qmp_chardev_open_serial(const CharDriver *driver,
+                                                const char *id,
                                                 ChardevBackend *backend,
                                                 ChardevReturn *ret,
                                                 bool *be_opened,
@@ -4544,7 +4614,8 @@ static CharDriverState *qmp_chardev_open_serial(const char *id,
 {
     ChardevHostdev *serial = backend->u.serial.data;
     ChardevCommon *common = qapi_ChardevHostdev_base(serial);
-    return qemu_chr_open_win_path(serial->device, common, errp);
+
+    return qemu_chr_open_win_path(driver, serial->device, common, errp);
 }
 
 #else /* WIN32 */
@@ -4561,7 +4632,8 @@ static int qmp_chardev_open_file_source(char *src, int flags,
     return fd;
 }
 
-static CharDriverState *qmp_chardev_open_file(const char *id,
+static CharDriverState *qmp_chardev_open_file(const CharDriver *driver,
+                                              const char *id,
                                               ChardevBackend *backend,
                                               ChardevReturn *ret,
                                               bool *be_opened,
@@ -4592,11 +4664,12 @@ static CharDriverState *qmp_chardev_open_file(const char *id,
         }
     }
 
-    return qemu_chr_open_fd(in, out, common, errp);
+    return qemu_chr_open_fd(driver, in, out, common, errp);
 }
 
 #ifdef HAVE_CHARDEV_SERIAL
-static CharDriverState *qmp_chardev_open_serial(const char *id,
+static CharDriverState *qmp_chardev_open_serial(const CharDriver *driver,
+                                                const char *id,
                                                 ChardevBackend *backend,
                                                 ChardevReturn *ret,
                                                 bool *be_opened,
@@ -4611,12 +4684,14 @@ static CharDriverState *qmp_chardev_open_serial(const char *id,
         return NULL;
     }
     qemu_set_nonblock(fd);
-    return qemu_chr_open_tty_fd(fd, common, be_opened, errp);
+
+    return qemu_chr_open_tty_fd(driver, fd, common, be_opened, errp);
 }
 #endif
 
 #ifdef HAVE_CHARDEV_PARPORT
-static CharDriverState *qmp_chardev_open_parallel(const char *id,
+static CharDriverState *qmp_chardev_open_parallel(const CharDriver *driver,
+                                                  const char *id,
                                                   ChardevBackend *backend,
                                                   ChardevReturn *ret,
                                                   bool *be_opened,
@@ -4630,12 +4705,62 @@ static CharDriverState *qmp_chardev_open_parallel(const char *id,
     if (fd < 0) {
         return NULL;
     }
-    return qemu_chr_open_pp_fd(fd, common, be_opened, errp);
+    return qemu_chr_open_pp_fd(driver, fd, common, be_opened, errp);
 }
+
+static const CharDriver parallel_driver = {
+    .kind = CHARDEV_BACKEND_KIND_PARALLEL,
+    .alias = "parport",
+    .parse = qemu_chr_parse_parallel,
+    .create = qmp_chardev_open_parallel,
+#if defined(__linux__)
+    .chr_write = null_chr_write,
+    .chr_ioctl = pp_ioctl,
+    .chr_free = pp_free,
+#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
+    .chr_write = null_chr_write,
+    .chr_ioctl = pp_ioctl,
+    /* FIXME: no chr_free */
+#endif
+};
 #endif
 
 #endif /* WIN32 */
 
+static const CharDriver file_driver = {
+    .kind = CHARDEV_BACKEND_KIND_FILE,
+    .parse = qemu_chr_parse_file_out,
+    .create = qmp_chardev_open_file,
+#ifdef _WIN32
+    .chr_write = win_chr_write,
+    /* FIXME: no chr_free */
+#else
+    .chr_add_watch = fd_chr_add_watch,
+    .chr_write = fd_chr_write,
+    .chr_update_read_handler = fd_chr_update_read_handler,
+    .chr_free = fd_chr_free,
+#endif
+};
+
+#ifdef HAVE_CHARDEV_SERIAL
+static const CharDriver serial_driver = {
+    .kind = CHARDEV_BACKEND_KIND_SERIAL,
+    .alias = "tty",
+    .parse = qemu_chr_parse_serial,
+    .create = qmp_chardev_open_serial,
+#ifdef _WIN32
+    .chr_write = win_chr_write,
+    .chr_free = win_chr_free,
+#else
+    .chr_add_watch = fd_chr_add_watch,
+    .chr_write = fd_chr_write,
+    .chr_update_read_handler = fd_chr_update_read_handler,
+    .chr_ioctl = tty_serial_ioctl,
+    .chr_free = qemu_chr_free_tty,
+#endif
+};
+#endif
+
 static gboolean socket_reconnect_timeout(gpointer opaque)
 {
     CharDriverState *chr = opaque;
@@ -4657,7 +4782,8 @@ static gboolean socket_reconnect_timeout(gpointer opaque)
     return false;
 }
 
-static CharDriverState *qmp_chardev_open_socket(const char *id,
+static CharDriverState *qmp_chardev_open_socket(const CharDriver *driver,
+                                                const char *id,
                                                 ChardevBackend *backend,
                                                 ChardevReturn *ret,
                                                 bool *be_opened,
@@ -4675,7 +4801,7 @@ static CharDriverState *qmp_chardev_open_socket(const char *id,
     ChardevCommon *common = qapi_ChardevSocket_base(sock);
     QIOChannelSocket *sioc = NULL;
 
-    chr = qemu_chr_alloc(common, errp);
+    chr = qemu_chr_alloc(driver, common, errp);
     if (!chr) {
         return NULL;
     }
@@ -4726,16 +4852,6 @@ static CharDriverState *qmp_chardev_open_socket(const char *id,
     }
 
     chr->opaque = s;
-    chr->chr_wait_connected = tcp_chr_wait_connected;
-    chr->chr_write = tcp_chr_write;
-    chr->chr_sync_read = tcp_chr_sync_read;
-    chr->chr_free = tcp_chr_free;
-    chr->chr_disconnect = tcp_chr_disconnect;
-    chr->get_msgfds = tcp_get_msgfds;
-    chr->set_msgfds = tcp_set_msgfds;
-    chr->chr_add_client = tcp_chr_add_client;
-    chr->chr_add_watch = tcp_chr_add_watch;
-    chr->chr_update_read_handler = tcp_chr_update_read_handler;
     /* be isn't opened until we get a connection */
     *be_opened = false;
 
@@ -4797,7 +4913,24 @@ static CharDriverState *qmp_chardev_open_socket(const char *id,
     return NULL;
 }
 
-static CharDriverState *qmp_chardev_open_udp(const char *id,
+static const CharDriver socket_driver = {
+    .kind = CHARDEV_BACKEND_KIND_SOCKET,
+    .parse = qemu_chr_parse_socket,
+    .create = qmp_chardev_open_socket,
+    .chr_wait_connected = tcp_chr_wait_connected,
+    .chr_write = tcp_chr_write,
+    .chr_sync_read = tcp_chr_sync_read,
+    .chr_disconnect = tcp_chr_disconnect,
+    .get_msgfds = tcp_get_msgfds,
+    .set_msgfds = tcp_set_msgfds,
+    .chr_add_client = tcp_chr_add_client,
+    .chr_add_watch = tcp_chr_add_watch,
+    .chr_update_read_handler = tcp_chr_update_read_handler,
+    .chr_free = tcp_chr_free,
+};
+
+static CharDriverState *qmp_chardev_open_udp(const CharDriver *driver,
+                                             const char *id,
                                              ChardevBackend *backend,
                                              ChardevReturn *ret,
                                              bool *be_opened,
@@ -4815,7 +4948,8 @@ static CharDriverState *qmp_chardev_open_udp(const char *id,
         object_unref(OBJECT(sioc));
         return NULL;
     }
-    chr = qemu_chr_open_udp(sioc, common, be_opened, errp);
+
+    chr = qemu_chr_open_udp(driver, sioc, common, be_opened, errp);
 
     name = g_strdup_printf("chardev-udp-%s", chr->label);
     qio_channel_set_name(QIO_CHANNEL(sioc), name);
@@ -4824,6 +4958,14 @@ static CharDriverState *qmp_chardev_open_udp(const char *id,
     return chr;
 }
 
+static const CharDriver udp_driver = {
+    .kind = CHARDEV_BACKEND_KIND_UDP,
+    .parse = qemu_chr_parse_udp,
+    .create = qmp_chardev_open_udp,
+    .chr_write = udp_chr_write,
+    .chr_update_read_handler = udp_chr_update_read_handler,
+    .chr_free = udp_chr_free,
+};
 
 bool qemu_chr_has_feature(CharDriverState *chr,
                           CharDriverFeature feature)
@@ -4859,7 +5001,7 @@ ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
         goto out_error;
     }
 
-    chr = cd->create(id, backend, ret, &be_opened, &local_err);
+    chr = cd->create(cd, id, backend, ret, &be_opened, &local_err);
     if (local_err) {
         error_propagate(errp, local_err);
         goto out_error;
@@ -4912,86 +5054,33 @@ void qemu_chr_cleanup(void)
 
 static void register_types(void)
 {
-    int i;
-    static const CharDriver drivers[] = {
-        {
-            .kind = CHARDEV_BACKEND_KIND_NULL,
-            .create = qemu_chr_open_null,
-        },
-        {
-            .kind = CHARDEV_BACKEND_KIND_SOCKET,
-            .parse = qemu_chr_parse_socket,
-            .create = qmp_chardev_open_socket,
-        },
-        {
-            .kind = CHARDEV_BACKEND_KIND_UDP,
-            .parse = qemu_chr_parse_udp,
-            .create = qmp_chardev_open_udp,
-        },
-        {
-            .kind = CHARDEV_BACKEND_KIND_RINGBUF,
-            .parse = qemu_chr_parse_ringbuf,
-            .create = qemu_chr_open_ringbuf,
-        },
-        {
-            .kind = CHARDEV_BACKEND_KIND_FILE,
-            .parse = qemu_chr_parse_file_out,
-            .create = qmp_chardev_open_file,
-        },
-        {
-            .kind = CHARDEV_BACKEND_KIND_STDIO,
-            .parse = qemu_chr_parse_stdio,
-            .create = qemu_chr_open_stdio,
-        },
-#if defined HAVE_CHARDEV_SERIAL
-        {
-            .kind = CHARDEV_BACKEND_KIND_SERIAL,
-            .alias = "tty",
-            .parse = qemu_chr_parse_serial,
-            .create = qmp_chardev_open_serial,
-        },
+    static const CharDriver *drivers[] = {
+        &null_driver,
+        &socket_driver,
+        &udp_driver,
+        &ringbuf_driver,
+        &file_driver,
+        &stdio_driver,
+#ifdef HAVE_CHARDEV_SERIAL
+        &serial_driver,
 #endif
 #ifdef HAVE_CHARDEV_PARPORT
-        {
-            .kind = CHARDEV_BACKEND_KIND_PARALLEL,
-            .alias = "parport",
-            .parse = qemu_chr_parse_parallel,
-            .create = qmp_chardev_open_parallel,
-        },
+        &parallel_driver,
 #endif
 #ifdef HAVE_CHARDEV_PTY
-        {
-            .kind = CHARDEV_BACKEND_KIND_PTY,
-            .create = qemu_chr_open_pty,
-        },
+        &pty_driver,
 #endif
 #ifdef _WIN32
-        {
-            .kind = CHARDEV_BACKEND_KIND_CONSOLE,
-            .create = qemu_chr_open_win_con,
-        },
+        &console_driver,
 #endif
-        {
-            .kind = CHARDEV_BACKEND_KIND_PIPE,
-            .parse = qemu_chr_parse_pipe,
-            .create = qemu_chr_open_pipe,
-        },
-        {
-            .kind = CHARDEV_BACKEND_KIND_MUX,
-            .parse = qemu_chr_parse_mux,
-            .create = qemu_chr_open_mux,
-        },
-        /* Bug-compatibility: */
-        {
-            .kind = CHARDEV_BACKEND_KIND_MEMORY,
-            .parse = qemu_chr_parse_ringbuf,
-            .create = qemu_chr_open_ringbuf,
-        },
+        &pipe_driver,
+        &mux_driver,
+        &memory_driver
     };
-
+    int i;
 
     for (i = 0; i < ARRAY_SIZE(drivers); i++) {
-        register_char_driver(&drivers[i]);
+        register_char_driver(drivers[i]);
     }
 
     /* this must be done after machine init, since we register FEs with muxes
diff --git a/spice-qemu-char.c b/spice-qemu-char.c
index 30db420..c7eb306 100644
--- a/spice-qemu-char.c
+++ b/spice-qemu-char.c
@@ -260,16 +260,15 @@ static void spice_chr_accept_input(struct CharDriverState *chr)
     spice_server_char_device_wakeup(&s->sin);
 }
 
-static CharDriverState *chr_open(const char *subtype,
-                                 void (*set_fe_open)(struct CharDriverState *,
-                                                     int),
+static CharDriverState *chr_open(const CharDriver *driver,
+                                 const char *subtype,
                                  ChardevCommon *backend,
                                  Error **errp)
 {
     CharDriverState *chr;
     SpiceCharDriver *s;
 
-    chr = qemu_chr_alloc(backend, errp);
+    chr = qemu_chr_alloc(driver, backend, errp);
     if (!chr) {
         return NULL;
     }
@@ -278,18 +277,14 @@ static CharDriverState *chr_open(const char *subtype,
     s->active = false;
     s->sin.subtype = g_strdup(subtype);
     chr->opaque = s;
-    chr->chr_write = spice_chr_write;
-    chr->chr_add_watch = spice_chr_add_watch;
-    chr->chr_free = spice_chr_free;
-    chr->chr_set_fe_open = set_fe_open;
-    chr->chr_accept_input = spice_chr_accept_input;
 
     QLIST_INSERT_HEAD(&spice_chars, s, next);
 
     return chr;
 }
 
-static CharDriverState *qemu_chr_open_spice_vmc(const char *id,
+static CharDriverState *qemu_chr_open_spice_vmc(const CharDriver *driver,
+                                                const char *id,
                                                 ChardevBackend *backend,
                                                 ChardevReturn *ret,
                                                 bool *be_opened,
@@ -312,11 +307,12 @@ static CharDriverState *qemu_chr_open_spice_vmc(const char *id,
     }
 
     *be_opened = false;
-    return chr_open(type, spice_vmc_set_fe_open, common, errp);
+    return chr_open(driver, type, common, errp);
 }
 
 #if SPICE_SERVER_VERSION >= 0x000c02
-static CharDriverState *qemu_chr_open_spice_port(const char *id,
+static CharDriverState *qemu_chr_open_spice_port(const CharDriver *driver,
+                                                 const char *id,
                                                  ChardevBackend *backend,
                                                  ChardevReturn *ret,
                                                  bool *be_opened,
@@ -333,7 +329,7 @@ static CharDriverState *qemu_chr_open_spice_port(const char *id,
         return NULL;
     }
 
-    chr = chr_open("port", spice_port_set_fe_open, common, errp);
+    chr = chr_open(driver, "port", common, errp);
     if (!chr) {
         return NULL;
     }
@@ -393,11 +389,21 @@ static void register_types(void)
         .kind = CHARDEV_BACKEND_KIND_SPICEVMC,
         .parse = qemu_chr_parse_spice_vmc,
         .create = qemu_chr_open_spice_vmc,
+        .chr_write = spice_chr_write,
+        .chr_add_watch = spice_chr_add_watch,
+        .chr_set_fe_open = spice_vmc_set_fe_open,
+        .chr_accept_input = spice_chr_accept_input,
+        .chr_free = spice_chr_free,
     };
     static const CharDriver port_driver = {
         .kind = CHARDEV_BACKEND_KIND_SPICEPORT,
         .parse = qemu_chr_parse_spice_port,
         .create = qemu_chr_open_spice_port,
+        .chr_write = spice_chr_write,
+        .chr_add_watch = spice_chr_add_watch,
+        .chr_set_fe_open = spice_port_set_fe_open,
+        .chr_accept_input = spice_chr_accept_input,
+        .chr_free = spice_chr_free,
     };
     register_char_driver(&vmc_driver);
     register_char_driver(&port_driver);
diff --git a/ui/console.c b/ui/console.c
index e4bb22f..f48ba26 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -1051,6 +1051,10 @@ static int console_puts(CharDriverState *chr, const uint8_t *buf, int len)
     QemuConsole *s = chr->opaque;
     int i;
 
+    if (!s->ds) {
+        return 0;
+    }
+
     s->update_x0 = s->width * FONT_WIDTH;
     s->update_y0 = s->height * FONT_HEIGHT;
     s->update_x1 = 0;
@@ -2000,8 +2004,6 @@ static void text_console_do_init(CharDriverState *chr, DisplayState *ds)
 
     s = chr->opaque;
 
-    chr->chr_write = console_puts;
-
     s->out_fifo.buf = s->out_fifo_buf;
     s->out_fifo.buf_size = sizeof(s->out_fifo_buf);
     s->kbd_timer = timer_new_ms(QEMU_CLOCK_REALTIME, kbd_send_chars, s);
@@ -2048,6 +2050,8 @@ static void text_console_do_init(CharDriverState *chr, DisplayState *ds)
     qemu_chr_be_generic_open(chr);
 }
 
+static const CharDriver vc_driver;
+
 static CharDriverState *text_console_init(ChardevVC *vc, Error **errp)
 {
     ChardevCommon *common = qapi_ChardevVC_base(vc);
@@ -2056,7 +2060,7 @@ static CharDriverState *text_console_init(ChardevVC *vc, Error **errp)
     unsigned width = 0;
     unsigned height = 0;
 
-    chr = qemu_chr_alloc(common, errp);
+    chr = qemu_chr_alloc(&vc_driver, common, errp);
     if (!chr) {
         return NULL;
     }
@@ -2089,7 +2093,6 @@ static CharDriverState *text_console_init(ChardevVC *vc, Error **errp)
 
     s->chr = chr;
     chr->opaque = s;
-    chr->chr_set_echo = text_console_set_echo;
 
     if (display_state) {
         text_console_do_init(chr, display_state);
@@ -2099,7 +2102,8 @@ static CharDriverState *text_console_init(ChardevVC *vc, Error **errp)
 
 static VcHandler *vc_handler = text_console_init;
 
-static CharDriverState *vc_init(const char *id, ChardevBackend *backend,
+static CharDriverState *vc_init(const CharDriver *driver,
+                                const char *id, ChardevBackend *backend,
                                 ChardevReturn *ret, bool *be_opened,
                                 Error **errp)
 {
@@ -2191,14 +2195,16 @@ static const TypeInfo qemu_console_info = {
     .class_size = sizeof(QemuConsoleClass),
 };
 
+static const CharDriver vc_driver = {
+    .kind = CHARDEV_BACKEND_KIND_VC,
+    .parse = qemu_chr_parse_vc,
+    .create = vc_init,
+    .chr_write = console_puts,
+    .chr_set_echo = text_console_set_echo,
+};
+
 static void register_types(void)
 {
-    static const CharDriver vc_driver = {
-        .kind = CHARDEV_BACKEND_KIND_VC,
-        .parse = qemu_chr_parse_vc,
-        .create = vc_init,
-    };
-
     type_register_static(&qemu_console_info);
     register_char_driver(&vc_driver);
 }
diff --git a/ui/gtk.c b/ui/gtk.c
index 86368e3..608400b 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -1703,6 +1703,12 @@ static CharDriverState *vcs[MAX_VCS];
 
 static CharDriverState *gd_vc_handler(ChardevVC *vc, Error **errp)
 {
+    static const CharDriver gd_vc_driver = {
+        .kind = CHARDEV_BACKEND_KIND_VC,
+        .chr_write = gd_vc_chr_write,
+        .chr_set_echo = gd_vc_chr_set_echo,
+    };
+
     ChardevCommon *common = qapi_ChardevVC_base(vc);
     CharDriverState *chr;
 
@@ -1711,14 +1717,11 @@ static CharDriverState *gd_vc_handler(ChardevVC *vc, Error **errp)
         return NULL;
     }
 
-    chr = qemu_chr_alloc(common, errp);
+    chr = qemu_chr_alloc(&gd_vc_driver, common, errp);
     if (!chr) {
         return NULL;
     }
 
-    chr->chr_write = gd_vc_chr_write;
-    chr->chr_set_echo = gd_vc_chr_set_echo;
-
     /* Temporary, until gd_vc_vte_init runs.  */
     chr->opaque = g_new0(VirtualConsole, 1);
 
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [Qemu-devel] [PULL 28/41] char: fold single-user functions in caller
  2017-01-27 13:45 [Qemu-devel] [PULL 00/41] Misc changes for 2017-01-27 Paolo Bonzini
                   ` (26 preceding siblings ...)
  2017-01-27 13:45 ` [Qemu-devel] [PULL 27/41] char: move callbacks in CharDriver Paolo Bonzini
@ 2017-01-27 13:45 ` Paolo Bonzini
  2017-01-27 13:45 ` [Qemu-devel] [PULL 29/41] char: introduce generic qemu_chr_get_kind() Paolo Bonzini
                   ` (13 subsequent siblings)
  41 siblings, 0 replies; 43+ messages in thread
From: Paolo Bonzini @ 2017-01-27 13:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau

From: Marc-André Lureau <marcandre.lureau@redhat.com>

This shortens the code a bit.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 qemu-char.c | 95 +++++++++++++++++++------------------------------------------
 1 file changed, 29 insertions(+), 66 deletions(-)

diff --git a/qemu-char.c b/qemu-char.c
index ce8d4bb..4719ad6 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -1891,22 +1891,6 @@ static void qemu_chr_free_tty(CharDriverState *chr)
 {
     fd_chr_free(chr);
 }
-
-static CharDriverState *qemu_chr_open_tty_fd(const CharDriver *driver,
-                                             int fd,
-                                             ChardevCommon *backend,
-                                             bool *be_opened,
-                                             Error **errp)
-{
-    CharDriverState *chr;
-
-    tty_serial_init(fd, 115200, 'N', 8, 1);
-    chr = qemu_chr_open_fd(driver, fd, fd, backend, errp);
-    if (!chr) {
-        return NULL;
-    }
-    return chr;
-}
 #endif /* __linux__ || __sun__ */
 
 #if defined(__linux__)
@@ -2327,29 +2311,6 @@ static int win_chr_poll(void *opaque)
     return 0;
 }
 
-static CharDriverState *qemu_chr_open_win_path(const CharDriver *driver,
-                                               const char *filename,
-                                               ChardevCommon *backend,
-                                               Error **errp)
-{
-    CharDriverState *chr;
-    WinCharState *s;
-
-    chr = qemu_chr_alloc(driver, backend, errp);
-    if (!chr) {
-        return NULL;
-    }
-    s = g_new0(WinCharState, 1);
-    chr->opaque = s;
-
-    if (win_chr_init(chr, filename, errp) < 0) {
-        g_free(s);
-        qemu_chr_free_common(chr);
-        return NULL;
-    }
-    return chr;
-}
-
 static int win_chr_pipe_poll(void *opaque)
 {
     CharDriverState *chr = opaque;
@@ -2806,30 +2767,6 @@ static void udp_chr_free(CharDriverState *chr)
     qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
 }
 
-static CharDriverState *qemu_chr_open_udp(const CharDriver *driver,
-                                          QIOChannelSocket *sioc,
-                                          ChardevCommon *backend,
-                                          bool *be_opened,
-                                          Error **errp)
-{
-    CharDriverState *chr = NULL;
-    NetCharDriver *s = NULL;
-
-    chr = qemu_chr_alloc(driver, backend, errp);
-    if (!chr) {
-        return NULL;
-    }
-    s = g_new0(NetCharDriver, 1);
-
-    s->ioc = QIO_CHANNEL(sioc);
-    s->bufcnt = 0;
-    s->bufptr = 0;
-    chr->opaque = s;
-    /* be isn't opened until we get a connection */
-    *be_opened = false;
-    return chr;
-}
-
 /***********************************************************/
 /* TCP Net console */
 
@@ -4614,8 +4551,23 @@ static CharDriverState *qmp_chardev_open_serial(const CharDriver *driver,
 {
     ChardevHostdev *serial = backend->u.serial.data;
     ChardevCommon *common = qapi_ChardevHostdev_base(serial);
+    CharDriverState *chr;
+    WinCharState *s;
+
+    chr = qemu_chr_alloc(driver, common, errp);
+    if (!chr) {
+        return NULL;
+    }
 
-    return qemu_chr_open_win_path(driver, serial->device, common, errp);
+    s = g_new0(WinCharState, 1);
+    chr->opaque = s;
+    if (win_chr_init(chr, serial->device, errp) < 0) {
+        g_free(s);
+        qemu_chr_free_common(chr);
+        return NULL;
+    }
+
+    return chr;
 }
 
 #else /* WIN32 */
@@ -4684,8 +4636,9 @@ static CharDriverState *qmp_chardev_open_serial(const CharDriver *driver,
         return NULL;
     }
     qemu_set_nonblock(fd);
+    tty_serial_init(fd, 115200, 'N', 8, 1);
 
-    return qemu_chr_open_tty_fd(driver, fd, common, be_opened, errp);
+    return qemu_chr_open_fd(driver, fd, fd, common, errp);
 }
 #endif
 
@@ -4941,6 +4894,7 @@ static CharDriverState *qmp_chardev_open_udp(const CharDriver *driver,
     QIOChannelSocket *sioc = qio_channel_socket_new();
     char *name;
     CharDriverState *chr;
+    NetCharDriver *s;
 
     if (qio_channel_socket_dgram_sync(sioc,
                                       udp->local, udp->remote,
@@ -4949,12 +4903,21 @@ static CharDriverState *qmp_chardev_open_udp(const CharDriver *driver,
         return NULL;
     }
 
-    chr = qemu_chr_open_udp(driver, sioc, common, be_opened, errp);
+    chr = qemu_chr_alloc(driver, common, errp);
+    if (!chr) {
+        return NULL;
+    }
 
     name = g_strdup_printf("chardev-udp-%s", chr->label);
     qio_channel_set_name(QIO_CHANNEL(sioc), name);
     g_free(name);
 
+    s = g_new0(NetCharDriver, 1);
+    s->ioc = QIO_CHANNEL(sioc);
+    chr->opaque = s;
+    /* be isn't opened until we get a connection */
+    *be_opened = false;
+
     return chr;
 }
 
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [Qemu-devel] [PULL 29/41] char: introduce generic qemu_chr_get_kind()
  2017-01-27 13:45 [Qemu-devel] [PULL 00/41] Misc changes for 2017-01-27 Paolo Bonzini
                   ` (27 preceding siblings ...)
  2017-01-27 13:45 ` [Qemu-devel] [PULL 28/41] char: fold single-user functions in caller Paolo Bonzini
@ 2017-01-27 13:45 ` Paolo Bonzini
  2017-01-27 13:45 ` [Qemu-devel] [PULL 30/41] char: use a feature bit for replay Paolo Bonzini
                   ` (12 subsequent siblings)
  41 siblings, 0 replies; 43+ messages in thread
From: Paolo Bonzini @ 2017-01-27 13:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau

From: Marc-André Lureau <marcandre.lureau@redhat.com>

This allows to remove the "is_mux" field from CharDriverState.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 include/sysemu/char.h | 15 +++++++++++++--
 monitor.c             |  2 +-
 qemu-char.c           | 21 ++++++++++-----------
 3 files changed, 24 insertions(+), 14 deletions(-)

diff --git a/include/sysemu/char.h b/include/sysemu/char.h
index 2dd0564..7d841ad 100644
--- a/include/sysemu/char.h
+++ b/include/sysemu/char.h
@@ -96,7 +96,6 @@ struct CharDriverState {
     char *filename;
     int logfd;
     int be_open;
-    int is_mux;
     guint fd_in_tag;
     bool replay;
     DECLARE_BITMAP(features, QEMU_CHAR_FEATURE_LAST);
@@ -455,7 +454,19 @@ void qemu_chr_be_generic_open(CharDriverState *s);
 void qemu_chr_fe_accept_input(CharBackend *be);
 int qemu_chr_add_client(CharDriverState *s, int fd);
 CharDriverState *qemu_chr_find(const char *name);
-bool chr_is_ringbuf(const CharDriverState *chr);
+
+/**
+ * @qemu_chr_get_kind:
+ *
+ * Returns the kind of char backend, or -1 if unspecified.
+ */
+ChardevBackendKind qemu_chr_get_kind(const CharDriverState *chr);
+
+static inline bool qemu_chr_is_ringbuf(const CharDriverState *chr)
+{
+    return qemu_chr_get_kind(chr) == CHARDEV_BACKEND_KIND_RINGBUF ||
+        qemu_chr_get_kind(chr) == CHARDEV_BACKEND_KIND_MEMORY;
+}
 
 bool qemu_chr_has_feature(CharDriverState *chr,
                           CharDriverFeature feature);
diff --git a/monitor.c b/monitor.c
index 6ac4e95..24add02 100644
--- a/monitor.c
+++ b/monitor.c
@@ -3196,7 +3196,7 @@ static void ringbuf_completion(ReadLineState *rs, const char *str)
 
         if (!strncmp(chr_info->label, str, len)) {
             CharDriverState *chr = qemu_chr_find(chr_info->label);
-            if (chr && chr_is_ringbuf(chr)) {
+            if (chr && qemu_chr_is_ringbuf(chr)) {
                 readline_add_completion(rs, chr_info->label);
             }
         }
diff --git a/qemu-char.c b/qemu-char.c
index 4719ad6..a7afb59 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -781,7 +781,7 @@ static void muxes_realize_done(Notifier *notifier, void *unused)
     CharDriverState *chr;
 
     QTAILQ_FOREACH(chr, &chardevs, next) {
-        if (chr->is_mux) {
+        if (qemu_chr_get_kind(chr) == CHARDEV_BACKEND_KIND_MUX) {
             MuxDriver *d = chr->opaque;
             int i;
 
@@ -888,7 +888,6 @@ static CharDriverState *qemu_chr_open_mux(const CharDriver *driver,
      * set of muxes
      */
     *be_opened = muxes_realized;
-    chr->is_mux = 1;
     if (!qemu_chr_fe_init(&d->chr, drv, errp)) {
         qemu_chr_free(chr);
         return NULL;
@@ -906,7 +905,7 @@ bool qemu_chr_fe_init(CharBackend *b, CharDriverState *s, Error **errp)
 {
     int tag = 0;
 
-    if (s->is_mux) {
+    if (qemu_chr_get_kind(s) == CHARDEV_BACKEND_KIND_MUX) {
         MuxDriver *d = s->opaque;
 
         if (d->mux_cnt >= MAX_MUX) {
@@ -933,7 +932,7 @@ unavailable:
 
 static bool qemu_chr_is_busy(CharDriverState *s)
 {
-    if (s->is_mux) {
+    if (qemu_chr_get_kind(s) == CHARDEV_BACKEND_KIND_MUX) {
         MuxDriver *d = s->opaque;
         return d->mux_cnt >= 0;
     } else {
@@ -950,7 +949,7 @@ void qemu_chr_fe_deinit(CharBackend *b)
         if (b->chr->be == b) {
             b->chr->be = NULL;
         }
-        if (b->chr->is_mux) {
+        if (qemu_chr_get_kind(b->chr) == CHARDEV_BACKEND_KIND_MUX) {
             MuxDriver *d = b->chr->opaque;
             d->backends[b->tag] = NULL;
         }
@@ -1001,7 +1000,7 @@ void qemu_chr_fe_set_handlers(CharBackend *b,
         }
     }
 
-    if (s->is_mux) {
+    if (qemu_chr_get_kind(s) == CHARDEV_BACKEND_KIND_MUX) {
         mux_chr_set_handlers(s, context);
     }
 }
@@ -1012,7 +1011,7 @@ void qemu_chr_fe_take_focus(CharBackend *b)
         return;
     }
 
-    if (b->chr->is_mux) {
+    if (qemu_chr_get_kind(b->chr) == CHARDEV_BACKEND_KIND_MUX) {
         mux_set_focus(b->chr, b->tag);
     }
 }
@@ -3559,9 +3558,9 @@ fail:
     return NULL;
 }
 
-bool chr_is_ringbuf(const CharDriverState *chr)
+ChardevBackendKind qemu_chr_get_kind(const CharDriverState *chr)
 {
-    return chr->driver->chr_write == ringbuf_chr_write;
+    return chr->driver->kind;
 }
 
 void qmp_ringbuf_write(const char *device, const char *data,
@@ -3579,7 +3578,7 @@ void qmp_ringbuf_write(const char *device, const char *data,
         return;
     }
 
-    if (!chr_is_ringbuf(chr)) {
+    if (!qemu_chr_is_ringbuf(chr)) {
         error_setg(errp,"%s is not a ringbuf device", device);
         return;
     }
@@ -3623,7 +3622,7 @@ char *qmp_ringbuf_read(const char *device, int64_t size,
         return NULL;
     }
 
-    if (!chr_is_ringbuf(chr)) {
+    if (!qemu_chr_is_ringbuf(chr)) {
         error_setg(errp,"%s is not a ringbuf device", device);
         return NULL;
     }
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [Qemu-devel] [PULL 30/41] char: use a feature bit for replay
  2017-01-27 13:45 [Qemu-devel] [PULL 00/41] Misc changes for 2017-01-27 Paolo Bonzini
                   ` (28 preceding siblings ...)
  2017-01-27 13:45 ` [Qemu-devel] [PULL 29/41] char: introduce generic qemu_chr_get_kind() Paolo Bonzini
@ 2017-01-27 13:45 ` Paolo Bonzini
  2017-01-27 13:45 ` [Qemu-devel] [PULL 31/41] char: allocate CharDriverState as a single object Paolo Bonzini
                   ` (11 subsequent siblings)
  41 siblings, 0 replies; 43+ messages in thread
From: Paolo Bonzini @ 2017-01-27 13:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Use a feature flag rather than a structure field for "replay".

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 include/sysemu/char.h |  3 ++-
 qemu-char.c           | 33 ++++++++++++++++++++-------------
 2 files changed, 22 insertions(+), 14 deletions(-)

diff --git a/include/sysemu/char.h b/include/sysemu/char.h
index 7d841ad..b0b4231 100644
--- a/include/sysemu/char.h
+++ b/include/sysemu/char.h
@@ -69,6 +69,8 @@ typedef enum {
     /* Whether it is possible to send/recv file descriptors
      * over the data channel */
     QEMU_CHAR_FEATURE_FD_PASS,
+    /* Whether replay or record mode is enabled */
+    QEMU_CHAR_FEATURE_REPLAY,
 
     QEMU_CHAR_FEATURE_LAST,
 } CharDriverFeature;
@@ -97,7 +99,6 @@ struct CharDriverState {
     int logfd;
     int be_open;
     guint fd_in_tag;
-    bool replay;
     DECLARE_BITMAP(features, QEMU_CHAR_FEATURE_LAST);
     QTAILQ_ENTRY(CharDriverState) next;
 };
diff --git a/qemu-char.c b/qemu-char.c
index a7afb59..5d04d82 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -277,6 +277,11 @@ static int qemu_chr_fe_write_buffer(CharDriverState *s, const uint8_t *buf, int
     return res;
 }
 
+static bool qemu_chr_replay(CharDriverState *chr)
+{
+    return qemu_chr_has_feature(chr, QEMU_CHAR_FEATURE_REPLAY);
+}
+
 int qemu_chr_fe_write(CharBackend *be, const uint8_t *buf, int len)
 {
     CharDriverState *s = be->chr;
@@ -286,7 +291,7 @@ int qemu_chr_fe_write(CharBackend *be, const uint8_t *buf, int len)
         return 0;
     }
 
-    if (s->replay && replay_mode == REPLAY_MODE_PLAY) {
+    if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_PLAY) {
         int offset;
         replay_char_write_event_load(&ret, &offset);
         assert(offset <= len);
@@ -303,7 +308,7 @@ int qemu_chr_fe_write(CharBackend *be, const uint8_t *buf, int len)
 
     qemu_mutex_unlock(&s->chr_write_lock);
     
-    if (s->replay && replay_mode == REPLAY_MODE_RECORD) {
+    if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_RECORD) {
         replay_char_write_event_save(ret, ret < 0 ? 0 : ret);
     }
     
@@ -315,7 +320,7 @@ static int qemu_chr_write_all(CharDriverState *s, const uint8_t *buf, int len)
     int offset;
     int res;
 
-    if (s->replay && replay_mode == REPLAY_MODE_PLAY) {
+    if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_PLAY) {
         replay_char_write_event_load(&res, &offset);
         assert(offset <= len);
         qemu_chr_fe_write_buffer(s, buf, offset, &offset);
@@ -324,7 +329,7 @@ static int qemu_chr_write_all(CharDriverState *s, const uint8_t *buf, int len)
 
     res = qemu_chr_fe_write_buffer(s, buf, len, &offset);
 
-    if (s->replay && replay_mode == REPLAY_MODE_RECORD) {
+    if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_RECORD) {
         replay_char_write_event_save(res, offset);
     }
 
@@ -355,7 +360,7 @@ int qemu_chr_fe_read_all(CharBackend *be, uint8_t *buf, int len)
         return 0;
     }
 
-    if (s->replay && replay_mode == REPLAY_MODE_PLAY) {
+    if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_PLAY) {
         return replay_char_read_all_load(buf);
     }
 
@@ -372,7 +377,7 @@ int qemu_chr_fe_read_all(CharBackend *be, uint8_t *buf, int len)
         }
 
         if (res < 0) {
-            if (s->replay && replay_mode == REPLAY_MODE_RECORD) {
+            if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_RECORD) {
                 replay_char_read_all_save_error(res);
             }
             return res;
@@ -385,7 +390,7 @@ int qemu_chr_fe_read_all(CharBackend *be, uint8_t *buf, int len)
         }
     }
 
-    if (s->replay && replay_mode == REPLAY_MODE_RECORD) {
+    if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_RECORD) {
         replay_char_read_all_save_buf(buf, offset);
     }
     return offset;
@@ -396,7 +401,7 @@ int qemu_chr_fe_ioctl(CharBackend *be, int cmd, void *arg)
     CharDriverState *s = be->chr;
     int res;
 
-    if (!s || !s->driver->chr_ioctl || s->replay) {
+    if (!s || !s->driver->chr_ioctl || qemu_chr_replay(s)) {
         res = -ENOTSUP;
     } else {
         res = s->driver->chr_ioctl(s, cmd, arg);
@@ -427,7 +432,7 @@ void qemu_chr_be_write_impl(CharDriverState *s, uint8_t *buf, int len)
 
 void qemu_chr_be_write(CharDriverState *s, uint8_t *buf, int len)
 {
-    if (s->replay) {
+    if (qemu_chr_replay(s)) {
         if (replay_mode == REPLAY_MODE_PLAY) {
             return;
         }
@@ -442,7 +447,7 @@ int qemu_chr_fe_get_msgfd(CharBackend *be)
     CharDriverState *s = be->chr;
     int fd;
     int res = (qemu_chr_fe_get_msgfds(be, &fd, 1) == 1) ? fd : -1;
-    if (s && s->replay) {
+    if (s && qemu_chr_replay(s)) {
         fprintf(stderr,
                 "Replay: get msgfd is not supported for serial devices yet\n");
         exit(1);
@@ -4249,8 +4254,10 @@ CharDriverState *qemu_chr_new(const char *label, const char *filename)
     CharDriverState *chr;
     chr = qemu_chr_new_noreplay(label, filename);
     if (chr) {
-        chr->replay = replay_mode != REPLAY_MODE_NONE;
-        if (chr->replay && chr->driver->chr_ioctl) {
+        if (replay_mode != REPLAY_MODE_NONE) {
+            qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_REPLAY);
+        }
+        if (qemu_chr_replay(chr) && chr->driver->chr_ioctl) {
             fprintf(stderr,
                     "Replay: ioctl is not supported for serial devices yet\n");
         }
@@ -4997,7 +5004,7 @@ void qmp_chardev_remove(const char *id, Error **errp)
         error_setg(errp, "Chardev '%s' is busy", id);
         return;
     }
-    if (chr->replay) {
+    if (qemu_chr_replay(chr)) {
         error_setg(errp,
             "Chardev '%s' cannot be unplugged in record/replay mode", id);
         return;
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [Qemu-devel] [PULL 31/41] char: allocate CharDriverState as a single object
  2017-01-27 13:45 [Qemu-devel] [PULL 00/41] Misc changes for 2017-01-27 Paolo Bonzini
                   ` (29 preceding siblings ...)
  2017-01-27 13:45 ` [Qemu-devel] [PULL 30/41] char: use a feature bit for replay Paolo Bonzini
@ 2017-01-27 13:45 ` Paolo Bonzini
  2017-01-27 13:45 ` [Qemu-devel] [PULL 32/41] bt: use qemu_chr_alloc() Paolo Bonzini
                   ` (10 subsequent siblings)
  41 siblings, 0 replies; 43+ messages in thread
From: Paolo Bonzini @ 2017-01-27 13:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Use a single allocation for CharDriverState, this avoids extra
allocations & pointers, and is a step towards more object-oriented
CharDriver.

Gtk console is a bit peculiar, gd_vc_chr_set_echo() used to have a
temporary VirtualConsole to save the echo bit. Instead now, we consider
whether vcd->console is set or not, and restore the echo bit saved in
VCDriverState when calling gd_vc_vte_init().

The casts added are temporary, they are replaced with QOM type-safe
macros in a later patch in this series.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 backends/baum.c       |  23 ++---
 backends/msmouse.c    |  16 +--
 backends/testdev.c    |  22 ++--
 gdbstub.c             |   1 +
 hw/bt/hci-csr.c       |  10 +-
 include/sysemu/char.h |   2 +-
 qemu-char.c           | 280 ++++++++++++++++++++++++++------------------------
 spice-qemu-char.c     |  39 +++----
 ui/console.c          |  21 ++--
 ui/gtk.c              |  30 ++++--
 10 files changed, 230 insertions(+), 214 deletions(-)

diff --git a/backends/baum.c b/backends/baum.c
index 109469e..4b48cf0 100644
--- a/backends/baum.c
+++ b/backends/baum.c
@@ -85,7 +85,7 @@
 #define BUF_SIZE 256
 
 typedef struct {
-    CharDriverState *chr;
+    CharDriverState parent;
 
     brlapi_handle_t *brlapi;
     int brlapi_fd;
@@ -255,7 +255,7 @@ static int baum_deferred_init(BaumDriverState *baum)
 /* The serial port can receive more of our data */
 static void baum_accept_input(struct CharDriverState *chr)
 {
-    BaumDriverState *baum = chr->opaque;
+    BaumDriverState *baum = (BaumDriverState *)chr;
     int room, first;
 
     if (!baum->out_buf_used)
@@ -281,22 +281,23 @@ static void baum_accept_input(struct CharDriverState *chr)
 /* We want to send a packet */
 static void baum_write_packet(BaumDriverState *baum, const uint8_t *buf, int len)
 {
+    CharDriverState *chr = (CharDriverState *)baum;
     uint8_t io_buf[1 + 2 * len], *cur = io_buf;
     int room;
     *cur++ = ESC;
     while (len--)
         if ((*cur++ = *buf++) == ESC)
             *cur++ = ESC;
-    room = qemu_chr_be_can_write(baum->chr);
+    room = qemu_chr_be_can_write(chr);
     len = cur - io_buf;
     if (len <= room) {
         /* Fits */
-        qemu_chr_be_write(baum->chr, io_buf, len);
+        qemu_chr_be_write(chr, io_buf, len);
     } else {
         int first;
         uint8_t out;
         /* Can't fit all, send what can be, and store the rest. */
-        qemu_chr_be_write(baum->chr, io_buf, room);
+        qemu_chr_be_write(chr, io_buf, room);
         len -= room;
         cur = io_buf + room;
         if (len > BUF_SIZE - baum->out_buf_used) {
@@ -471,7 +472,7 @@ static int baum_eat_packet(BaumDriverState *baum, const uint8_t *buf, int len)
 /* The other end is writing some data.  Store it and try to interpret */
 static int baum_write(CharDriverState *chr, const uint8_t *buf, int len)
 {
-    BaumDriverState *baum = chr->opaque;
+    BaumDriverState *baum = (BaumDriverState *)chr;
     int tocopy, cur, eaten, orig_len = len;
 
     if (!len)
@@ -612,14 +613,13 @@ static void baum_chr_read(void *opaque)
 
 static void baum_free(struct CharDriverState *chr)
 {
-    BaumDriverState *baum = chr->opaque;
+    BaumDriverState *baum = (BaumDriverState *)chr;
 
     timer_free(baum->cellCount_timer);
     if (baum->brlapi) {
         brlapi__closeConnection(baum->brlapi);
         g_free(baum->brlapi);
     }
-    g_free(baum);
 }
 
 static CharDriverState *chr_baum_init(const CharDriver *driver,
@@ -638,10 +638,7 @@ static CharDriverState *chr_baum_init(const CharDriver *driver,
     if (!chr) {
         return NULL;
     }
-    baum = g_malloc0(sizeof(BaumDriverState));
-    baum->chr = chr;
-
-    chr->opaque = baum;
+    baum = (BaumDriverState *)chr;
 
     handle = g_malloc0(brlapi_getHandleSize());
     baum->brlapi = handle;
@@ -663,13 +660,13 @@ static CharDriverState *chr_baum_init(const CharDriver *driver,
 fail_handle:
     g_free(handle);
     g_free(chr);
-    g_free(baum);
     return NULL;
 }
 
 static void register_types(void)
 {
     static const CharDriver driver = {
+        .instance_size = sizeof(BaumDriverState),
         .kind = CHARDEV_BACKEND_KIND_BRAILLE,
         .create = chr_baum_init,
         .chr_write = baum_write,
diff --git a/backends/msmouse.c b/backends/msmouse.c
index 2c238ba..30087b3 100644
--- a/backends/msmouse.c
+++ b/backends/msmouse.c
@@ -31,7 +31,8 @@
 #define MSMOUSE_HI2(n) (((n) & 0xc0) >> 6)
 
 typedef struct {
-    CharDriverState *chr;
+    CharDriverState parent;
+
     QemuInputHandlerState *hs;
     int axis[INPUT_AXIS__MAX];
     bool btns[INPUT_BUTTON__MAX];
@@ -42,7 +43,7 @@ typedef struct {
 
 static void msmouse_chr_accept_input(CharDriverState *chr)
 {
-    MouseState *mouse = chr->opaque;
+    MouseState *mouse = (MouseState *)chr;
     int len;
 
     len = qemu_chr_be_can_write(chr);
@@ -122,9 +123,10 @@ static void msmouse_input_event(DeviceState *dev, QemuConsole *src,
 static void msmouse_input_sync(DeviceState *dev)
 {
     MouseState *mouse = (MouseState *)dev;
+    CharDriverState *chr = (CharDriverState *)dev;
 
     msmouse_queue_event(mouse);
-    msmouse_chr_accept_input(mouse->chr);
+    msmouse_chr_accept_input(chr);
 }
 
 static int msmouse_chr_write (struct CharDriverState *s, const uint8_t *buf, int len)
@@ -135,10 +137,9 @@ static int msmouse_chr_write (struct CharDriverState *s, const uint8_t *buf, int
 
 static void msmouse_chr_free(struct CharDriverState *chr)
 {
-    MouseState *mouse = chr->opaque;
+    MouseState *mouse = (MouseState *)chr;
 
     qemu_input_handler_unregister(mouse->hs);
-    g_free(mouse);
 }
 
 static QemuInputHandler msmouse_handler = {
@@ -165,12 +166,10 @@ static CharDriverState *qemu_chr_open_msmouse(const CharDriver *driver,
     }
     *be_opened = false;
 
-    mouse = g_new0(MouseState, 1);
+    mouse = (MouseState *)chr;
     mouse->hs = qemu_input_handler_register((DeviceState *)mouse,
                                             &msmouse_handler);
 
-    mouse->chr = chr;
-    chr->opaque = mouse;
 
     return chr;
 }
@@ -178,6 +177,7 @@ static CharDriverState *qemu_chr_open_msmouse(const CharDriver *driver,
 static void register_types(void)
 {
     static const CharDriver driver = {
+        .instance_size = sizeof(MouseState),
         .kind = CHARDEV_BACKEND_KIND_MSMOUSE,
         .create = qemu_chr_open_msmouse,
         .chr_write = msmouse_chr_write,
diff --git a/backends/testdev.c b/backends/testdev.c
index 2339693..dfa4fe1 100644
--- a/backends/testdev.c
+++ b/backends/testdev.c
@@ -30,7 +30,8 @@
 #define BUF_SIZE 32
 
 typedef struct {
-    CharDriverState *chr;
+    CharDriverState parent;
+
     uint8_t in_buf[32];
     int in_buf_used;
 } TestdevCharState;
@@ -79,7 +80,7 @@ static int testdev_eat_packet(TestdevCharState *testdev)
 /* The other end is writing some data.  Store it and try to interpret */
 static int testdev_write(CharDriverState *chr, const uint8_t *buf, int len)
 {
-    TestdevCharState *testdev = chr->opaque;
+    TestdevCharState *testdev = (TestdevCharState *)chr;
     int tocopy, eaten, orig_len = len;
 
     while (len) {
@@ -102,13 +103,6 @@ static int testdev_write(CharDriverState *chr, const uint8_t *buf, int len)
     return orig_len;
 }
 
-static void testdev_free(struct CharDriverState *chr)
-{
-    TestdevCharState *testdev = chr->opaque;
-
-    g_free(testdev);
-}
-
 static CharDriverState *chr_testdev_init(const CharDriver *driver,
                                          const char *id,
                                          ChardevBackend *backend,
@@ -116,14 +110,10 @@ static CharDriverState *chr_testdev_init(const CharDriver *driver,
                                          bool *be_opened,
                                          Error **errp)
 {
-    TestdevCharState *testdev;
-    CharDriverState *chr;
-
-    testdev = g_new0(TestdevCharState, 1);
-    testdev->chr = chr = g_new0(CharDriverState, 1);
+    TestdevCharState *testdev = g_new0(TestdevCharState, 1);;
+    CharDriverState *chr = (CharDriverState *)testdev;
 
     chr->driver = driver;
-    chr->opaque = testdev;
 
     return chr;
 }
@@ -131,10 +121,10 @@ static CharDriverState *chr_testdev_init(const CharDriver *driver,
 static void register_types(void)
 {
     static const CharDriver driver = {
+        .instance_size = sizeof(TestdevCharState),
         .kind = CHARDEV_BACKEND_KIND_TESTDEV,
         .create = chr_testdev_init,
         .chr_write = testdev_write,
-        .chr_free = testdev_free,
     };
     register_char_driver(&driver);
 }
diff --git a/gdbstub.c b/gdbstub.c
index 3de9bcb..719e413 100644
--- a/gdbstub.c
+++ b/gdbstub.c
@@ -1732,6 +1732,7 @@ int gdbserver_start(const char *device)
     CharDriverState *mon_chr;
     ChardevCommon common = { 0 };
     static const CharDriver driver = {
+        .instance_size = sizeof(CharDriverState),
         .kind = -1,
         .chr_write = gdb_monitor_write,
     };
diff --git a/hw/bt/hci-csr.c b/hw/bt/hci-csr.c
index 9c3fb3c..bf2deb0 100644
--- a/hw/bt/hci-csr.c
+++ b/hw/bt/hci-csr.c
@@ -28,11 +28,11 @@
 #include "hw/bt.h"
 
 struct csrhci_s {
+    CharDriverState chr;
     int enable;
     qemu_irq *pins;
     int pin_state;
     int modem_state;
-    CharDriverState chr;
 #define FIFO_LEN	4096
     int out_start;
     int out_len;
@@ -314,7 +314,7 @@ static void csrhci_ready_for_next_inpkt(struct csrhci_s *s)
 static int csrhci_write(struct CharDriverState *chr,
                 const uint8_t *buf, int len)
 {
-    struct csrhci_s *s = (struct csrhci_s *) chr->opaque;
+    struct csrhci_s *s = (struct csrhci_s *)chr;
     int total = 0;
 
     if (!s->enable)
@@ -387,7 +387,7 @@ static void csrhci_out_hci_packet_acl(void *opaque,
 static int csrhci_ioctl(struct CharDriverState *chr, int cmd, void *arg)
 {
     QEMUSerialSetParams *ssp;
-    struct csrhci_s *s = (struct csrhci_s *) chr->opaque;
+    struct csrhci_s *s = (struct csrhci_s *) chr;
     int prev_state = s->modem_state;
 
     switch (cmd) {
@@ -455,7 +455,7 @@ static void csrhci_pins(void *opaque, int line, int level)
 
 qemu_irq *csrhci_pins_get(CharDriverState *chr)
 {
-    struct csrhci_s *s = (struct csrhci_s *) chr->opaque;
+    struct csrhci_s *s = (struct csrhci_s *) chr;
 
     return s->pins;
 }
@@ -463,6 +463,7 @@ qemu_irq *csrhci_pins_get(CharDriverState *chr)
 CharDriverState *uart_hci_init(void)
 {
     static const CharDriver hci_driver = {
+        .instance_size = sizeof(struct csrhci_s),
         .kind = -1,
         .chr_write = csrhci_write,
         .chr_ioctl = csrhci_ioctl,
@@ -470,7 +471,6 @@ CharDriverState *uart_hci_init(void)
     struct csrhci_s *s = (struct csrhci_s *)
             g_malloc0(sizeof(struct csrhci_s));
 
-    s->chr.opaque = s;
     s->chr.driver = &hci_driver;
 
     s->hci = qemu_next_hci();
diff --git a/include/sysemu/char.h b/include/sysemu/char.h
index b0b4231..8bb94e5 100644
--- a/include/sysemu/char.h
+++ b/include/sysemu/char.h
@@ -93,7 +93,6 @@ struct CharDriverState {
     const CharDriver *driver;
     QemuMutex chr_write_lock;
     CharBackend *be;
-    void *opaque;
     char *label;
     char *filename;
     int logfd;
@@ -484,6 +483,7 @@ struct CharDriver {
                                ChardevBackend *backend,
                                ChardevReturn *ret, bool *be_opened,
                                Error **errp);
+    size_t instance_size;
 
     int (*chr_write)(struct CharDriverState *s, const uint8_t *buf, int len);
     int (*chr_sync_read)(struct CharDriverState *s,
diff --git a/qemu-char.c b/qemu-char.c
index 5d04d82..dfd1b4c 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -165,12 +165,14 @@ static void qemu_chr_free_common(CharDriverState *chr);
 CharDriverState *qemu_chr_alloc(const CharDriver *driver,
                                 ChardevCommon *backend, Error **errp)
 {
-    CharDriverState *chr = g_malloc0(sizeof(CharDriverState));
-    qemu_mutex_init(&chr->chr_write_lock);
+    CharDriverState *chr;
 
     assert(driver);
     assert(driver->chr_write);
+    assert(driver->instance_size >= sizeof(CharDriverState));
 
+    chr = g_malloc0(driver->instance_size);
+    qemu_mutex_init(&chr->chr_write_lock);
     if (backend->has_logfile) {
         int flags = O_WRONLY | O_CREAT;
         if (backend->has_logappend &&
@@ -536,6 +538,7 @@ static CharDriverState *qemu_chr_open_null(const CharDriver *driver,
 }
 
 static const CharDriver null_driver = {
+    .instance_size = sizeof(CharDriverState),
     .kind = CHARDEV_BACKEND_KIND_NULL,
     .create = qemu_chr_open_null,
     .chr_write = null_chr_write,
@@ -546,6 +549,7 @@ static const CharDriver null_driver = {
 #define MUX_BUFFER_SIZE 32	/* Must be a power of 2.  */
 #define MUX_BUFFER_MASK (MUX_BUFFER_SIZE - 1)
 struct MuxDriver {
+    CharDriverState parent;
     CharBackend *backends[MAX_MUX];
     CharBackend chr;
     int focus;
@@ -568,7 +572,7 @@ struct MuxDriver {
 /* Called with chr_write_lock held.  */
 static int mux_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
 {
-    MuxDriver *d = chr->opaque;
+    MuxDriver *d = (MuxDriver *)chr;
     int ret;
     if (!d->timestamps) {
         ret = qemu_chr_fe_write(&d->chr, buf, len);
@@ -702,7 +706,7 @@ static int mux_proc_byte(CharDriverState *chr, MuxDriver *d, int ch)
 
 static void mux_chr_accept_input(CharDriverState *chr)
 {
-    MuxDriver *d = chr->opaque;
+    MuxDriver *d = (MuxDriver *)chr;
     int m = d->focus;
     CharBackend *be = d->backends[m];
 
@@ -715,8 +719,7 @@ static void mux_chr_accept_input(CharDriverState *chr)
 
 static int mux_chr_can_read(void *opaque)
 {
-    CharDriverState *chr = opaque;
-    MuxDriver *d = chr->opaque;
+    MuxDriver *d = opaque;
     int m = d->focus;
     CharBackend *be = d->backends[m];
 
@@ -734,7 +737,7 @@ static int mux_chr_can_read(void *opaque)
 static void mux_chr_read(void *opaque, const uint8_t *buf, int size)
 {
     CharDriverState *chr = opaque;
-    MuxDriver *d = chr->opaque;
+    MuxDriver *d = opaque;
     int m = d->focus;
     CharBackend *be = d->backends[m];
     int i;
@@ -756,8 +759,7 @@ static bool muxes_realized;
 
 static void mux_chr_event(void *opaque, int event)
 {
-    CharDriverState *chr = opaque;
-    MuxDriver *d = chr->opaque;
+    MuxDriver *d = opaque;
     int i;
 
     if (!muxes_realized) {
@@ -787,7 +789,7 @@ static void muxes_realize_done(Notifier *notifier, void *unused)
 
     QTAILQ_FOREACH(chr, &chardevs, next) {
         if (qemu_chr_get_kind(chr) == CHARDEV_BACKEND_KIND_MUX) {
-            MuxDriver *d = chr->opaque;
+            MuxDriver *d = (MuxDriver *)chr;
             int i;
 
             /* send OPENED to all already-attached FEs */
@@ -809,7 +811,7 @@ static Notifier muxes_realize_notify = {
 
 static GSource *mux_chr_add_watch(CharDriverState *s, GIOCondition cond)
 {
-    MuxDriver *d = s->opaque;
+    MuxDriver *d = (MuxDriver *)s;
     CharDriverState *chr = qemu_chr_fe_get_driver(&d->chr);
 
     if (!chr->driver->chr_add_watch) {
@@ -821,7 +823,7 @@ static GSource *mux_chr_add_watch(CharDriverState *s, GIOCondition cond)
 
 static void mux_chr_free(struct CharDriverState *chr)
 {
-    MuxDriver *d = chr->opaque;
+    MuxDriver *d = (MuxDriver *)chr;
     int i;
 
     for (i = 0; i < d->mux_cnt; i++) {
@@ -831,12 +833,11 @@ static void mux_chr_free(struct CharDriverState *chr)
         }
     }
     qemu_chr_fe_deinit(&d->chr);
-    g_free(d);
 }
 
 static void mux_chr_set_handlers(CharDriverState *chr, GMainContext *context)
 {
-    MuxDriver *d = chr->opaque;
+    MuxDriver *d = (MuxDriver *)chr;
 
     /* Fix up the real driver with mux routines */
     qemu_chr_fe_set_handlers(&d->chr,
@@ -849,7 +850,7 @@ static void mux_chr_set_handlers(CharDriverState *chr, GMainContext *context)
 
 static void mux_set_focus(CharDriverState *chr, int focus)
 {
-    MuxDriver *d = chr->opaque;
+    MuxDriver *d = (MuxDriver *)chr;
 
     assert(focus >= 0);
     assert(focus < d->mux_cnt);
@@ -885,9 +886,7 @@ static CharDriverState *qemu_chr_open_mux(const CharDriver *driver,
     if (!chr) {
         return NULL;
     }
-    d = g_new0(MuxDriver, 1);
-
-    chr->opaque = d;
+    d = (MuxDriver *)chr;
     d->focus = -1;
     /* only default to opened state if we've realized the initial
      * set of muxes
@@ -911,7 +910,7 @@ bool qemu_chr_fe_init(CharBackend *b, CharDriverState *s, Error **errp)
     int tag = 0;
 
     if (qemu_chr_get_kind(s) == CHARDEV_BACKEND_KIND_MUX) {
-        MuxDriver *d = s->opaque;
+        MuxDriver *d = (MuxDriver *)s;
 
         if (d->mux_cnt >= MAX_MUX) {
             goto unavailable;
@@ -938,7 +937,7 @@ unavailable:
 static bool qemu_chr_is_busy(CharDriverState *s)
 {
     if (qemu_chr_get_kind(s) == CHARDEV_BACKEND_KIND_MUX) {
-        MuxDriver *d = s->opaque;
+        MuxDriver *d = (MuxDriver *)s;
         return d->mux_cnt >= 0;
     } else {
         return s->be != NULL;
@@ -955,7 +954,7 @@ void qemu_chr_fe_deinit(CharBackend *b)
             b->chr->be = NULL;
         }
         if (qemu_chr_get_kind(b->chr) == CHARDEV_BACKEND_KIND_MUX) {
-            MuxDriver *d = b->chr->opaque;
+            MuxDriver *d = (MuxDriver *)b->chr;
             d->backends[b->tag] = NULL;
         }
         b->chr = NULL;
@@ -1195,6 +1194,7 @@ static int io_channel_send(QIOChannel *ioc, const void *buf, size_t len)
 
 
 typedef struct FDCharDriver {
+    CharDriverState parent;
     CharDriverState *chr;
     QIOChannel *ioc_in, *ioc_out;
     int max_size;
@@ -1203,15 +1203,15 @@ typedef struct FDCharDriver {
 /* Called with chr_write_lock held.  */
 static int fd_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
 {
-    FDCharDriver *s = chr->opaque;
-    
+    FDCharDriver *s = (FDCharDriver *)chr;
+
     return io_channel_send(s->ioc_out, buf, len);
 }
 
 static gboolean fd_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
 {
     CharDriverState *chr = opaque;
-    FDCharDriver *s = chr->opaque;
+    FDCharDriver *s = opaque;
     int len;
     uint8_t buf[READ_BUF_LEN];
     ssize_t ret;
@@ -1241,7 +1241,7 @@ static gboolean fd_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
 static int fd_chr_read_poll(void *opaque)
 {
     CharDriverState *chr = opaque;
-    FDCharDriver *s = chr->opaque;
+    FDCharDriver *s = opaque;
 
     s->max_size = qemu_chr_be_can_write(chr);
     return s->max_size;
@@ -1249,14 +1249,14 @@ static int fd_chr_read_poll(void *opaque)
 
 static GSource *fd_chr_add_watch(CharDriverState *chr, GIOCondition cond)
 {
-    FDCharDriver *s = chr->opaque;
+    FDCharDriver *s = (FDCharDriver *)chr;
     return qio_channel_create_watch(s->ioc_out, cond);
 }
 
 static void fd_chr_update_read_handler(CharDriverState *chr,
                                        GMainContext *context)
 {
-    FDCharDriver *s = chr->opaque;
+    FDCharDriver *s = (FDCharDriver *)chr;
 
     remove_fd_in_watch(chr);
     if (s->ioc_in) {
@@ -1269,7 +1269,7 @@ static void fd_chr_update_read_handler(CharDriverState *chr,
 
 static void fd_chr_free(struct CharDriverState *chr)
 {
-    FDCharDriver *s = chr->opaque;
+    FDCharDriver *s = (FDCharDriver *)chr;
 
     remove_fd_in_watch(chr);
     if (s->ioc_in) {
@@ -1279,7 +1279,6 @@ static void fd_chr_free(struct CharDriverState *chr)
         object_unref(OBJECT(s->ioc_out));
     }
 
-    g_free(s);
     qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
 }
 
@@ -1296,7 +1295,7 @@ static CharDriverState *qemu_chr_open_fd(const CharDriver *driver,
     if (!chr) {
         return NULL;
     }
-    s = g_new0(FDCharDriver, 1);
+    s = (FDCharDriver *)chr;
     s->ioc_in = QIO_CHANNEL(qio_channel_file_new_fd(fd_in));
     name = g_strdup_printf("chardev-file-in-%s", chr->label);
     qio_channel_set_name(QIO_CHANNEL(s->ioc_in), name);
@@ -1307,7 +1306,6 @@ static CharDriverState *qemu_chr_open_fd(const CharDriver *driver,
     g_free(name);
     qemu_set_nonblock(fd_out);
     s->chr = chr;
-    chr->opaque = s;
 
     return chr;
 }
@@ -1448,6 +1446,7 @@ static CharDriverState *qemu_chr_open_stdio(const CharDriver *driver,
 #define HAVE_CHARDEV_PTY 1
 
 typedef struct {
+    CharDriverState parent;
     QIOChannel *ioc;
     int read_bytes;
 
@@ -1463,7 +1462,7 @@ static void pty_chr_state(CharDriverState *chr, int connected);
 static gboolean pty_chr_timer(gpointer opaque)
 {
     struct CharDriverState *chr = opaque;
-    PtyCharDriver *s = chr->opaque;
+    PtyCharDriver *s = opaque;
 
     qemu_mutex_lock(&chr->chr_write_lock);
     s->timer_tag = 0;
@@ -1479,7 +1478,7 @@ static gboolean pty_chr_timer(gpointer opaque)
 /* Called with chr_write_lock held.  */
 static void pty_chr_rearm_timer(CharDriverState *chr, int ms)
 {
-    PtyCharDriver *s = chr->opaque;
+    PtyCharDriver *s = (PtyCharDriver *)chr;
     char *name;
 
     if (s->timer_tag) {
@@ -1501,7 +1500,7 @@ static void pty_chr_rearm_timer(CharDriverState *chr, int ms)
 /* Called with chr_write_lock held.  */
 static void pty_chr_update_read_handler_locked(CharDriverState *chr)
 {
-    PtyCharDriver *s = chr->opaque;
+    PtyCharDriver *s = (PtyCharDriver *)chr;
     GPollFD pfd;
     int rc;
     QIOChannelFile *fioc = QIO_CHANNEL_FILE(s->ioc);
@@ -1532,7 +1531,7 @@ static void pty_chr_update_read_handler(CharDriverState *chr,
 /* Called with chr_write_lock held.  */
 static int pty_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
 {
-    PtyCharDriver *s = chr->opaque;
+    PtyCharDriver *s = (PtyCharDriver *)chr;
 
     if (!s->connected) {
         /* guest sends data, check for (re-)connect */
@@ -1546,7 +1545,7 @@ static int pty_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
 
 static GSource *pty_chr_add_watch(CharDriverState *chr, GIOCondition cond)
 {
-    PtyCharDriver *s = chr->opaque;
+    PtyCharDriver *s = (PtyCharDriver *)chr;
     if (!s->connected) {
         return NULL;
     }
@@ -1556,7 +1555,7 @@ static GSource *pty_chr_add_watch(CharDriverState *chr, GIOCondition cond)
 static int pty_chr_read_poll(void *opaque)
 {
     CharDriverState *chr = opaque;
-    PtyCharDriver *s = chr->opaque;
+    PtyCharDriver *s = opaque;
 
     s->read_bytes = qemu_chr_be_can_write(chr);
     return s->read_bytes;
@@ -1565,7 +1564,7 @@ static int pty_chr_read_poll(void *opaque)
 static gboolean pty_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
 {
     CharDriverState *chr = opaque;
-    PtyCharDriver *s = chr->opaque;
+    PtyCharDriver *s = opaque;
     gsize len;
     uint8_t buf[READ_BUF_LEN];
     ssize_t ret;
@@ -1590,7 +1589,7 @@ static gboolean pty_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
 static gboolean qemu_chr_be_generic_open_func(gpointer opaque)
 {
     CharDriverState *chr = opaque;
-    PtyCharDriver *s = chr->opaque;
+    PtyCharDriver *s = opaque;
 
     s->open_tag = 0;
     qemu_chr_be_generic_open(chr);
@@ -1600,7 +1599,7 @@ static gboolean qemu_chr_be_generic_open_func(gpointer opaque)
 /* Called with chr_write_lock held.  */
 static void pty_chr_state(CharDriverState *chr, int connected)
 {
-    PtyCharDriver *s = chr->opaque;
+    PtyCharDriver *s = (PtyCharDriver *)chr;
 
     if (!connected) {
         if (s->open_tag) {
@@ -1634,7 +1633,7 @@ static void pty_chr_state(CharDriverState *chr, int connected)
 
 static void pty_chr_free(struct CharDriverState *chr)
 {
-    PtyCharDriver *s = chr->opaque;
+    PtyCharDriver *s = (PtyCharDriver *)chr;
 
     qemu_mutex_lock(&chr->chr_write_lock);
     pty_chr_state(chr, 0);
@@ -1644,7 +1643,6 @@ static void pty_chr_free(struct CharDriverState *chr)
         s->timer_tag = 0;
     }
     qemu_mutex_unlock(&chr->chr_write_lock);
-    g_free(s);
     qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
 }
 
@@ -1684,20 +1682,19 @@ static CharDriverState *qemu_chr_open_pty(const CharDriver *driver,
     fprintf(stderr, "char device redirected to %s (label %s)\n",
             pty_name, id);
 
-    s = g_new0(PtyCharDriver, 1);
-    chr->opaque = s;
-    *be_opened = false;
-
+    s = (PtyCharDriver *)chr;
     s->ioc = QIO_CHANNEL(qio_channel_file_new_fd(master_fd));
     name = g_strdup_printf("chardev-pty-%s", chr->label);
     qio_channel_set_name(QIO_CHANNEL(s->ioc), name);
     g_free(name);
     s->timer_tag = 0;
+    *be_opened = false;
 
     return chr;
 }
 
 static const CharDriver pty_driver = {
+    .instance_size = sizeof(PtyCharDriver),
     .kind = CHARDEV_BACKEND_KIND_PTY,
     .create = qemu_chr_open_pty,
     .chr_write = pty_chr_write,
@@ -1823,7 +1820,7 @@ static void tty_serial_init(int fd, int speed,
 
 static int tty_serial_ioctl(CharDriverState *chr, int cmd, void *arg)
 {
-    FDCharDriver *s = chr->opaque;
+    FDCharDriver *s = (FDCharDriver *)chr;
     QIOChannelFile *fioc = QIO_CHANNEL_FILE(s->ioc_in);
 
     switch(cmd) {
@@ -1902,6 +1899,7 @@ static void qemu_chr_free_tty(CharDriverState *chr)
 #define HAVE_CHARDEV_PARPORT 1
 
 typedef struct {
+    CharDriverState parent;
     int fd;
     int mode;
 } ParallelCharDriver;
@@ -1919,7 +1917,7 @@ static int pp_hw_mode(ParallelCharDriver *s, uint16_t mode)
 
 static int pp_ioctl(CharDriverState *chr, int cmd, void *arg)
 {
-    ParallelCharDriver *drv = chr->opaque;
+    ParallelCharDriver *drv = (ParallelCharDriver *)chr;
     int fd = drv->fd;
     uint8_t b;
 
@@ -2000,13 +1998,12 @@ static int pp_ioctl(CharDriverState *chr, int cmd, void *arg)
 
 static void pp_free(CharDriverState *chr)
 {
-    ParallelCharDriver *drv = chr->opaque;
+    ParallelCharDriver *drv = (ParallelCharDriver *)chr;
     int fd = drv->fd;
 
     pp_hw_mode(drv, IEEE1284_MODE_COMPAT);
     ioctl(fd, PPRELEASE);
     close(fd);
-    g_free(drv);
     qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
 }
 
@@ -2030,9 +2027,7 @@ static CharDriverState *qemu_chr_open_pp_fd(const CharDriver *driver,
         return NULL;
     }
 
-    drv = g_new0(ParallelCharDriver, 1);
-    chr->opaque = drv;
-
+    drv = (ParallelCharDriver *)chr;
     drv->fd = fd;
     drv->mode = IEEE1284_MODE_COMPAT;
 
@@ -2044,35 +2039,45 @@ static CharDriverState *qemu_chr_open_pp_fd(const CharDriver *driver,
 
 #define HAVE_CHARDEV_PARPORT 1
 
+typedef struct {
+    CharDriverState parent;
+    int fd;
+} ParallelCharDriver;
+
 static int pp_ioctl(CharDriverState *chr, int cmd, void *arg)
 {
-    int fd = (int)(intptr_t)chr->opaque;
+    ParallelCharDriver *drv = (ParallelCharDriver *)chr;
     uint8_t b;
 
-    switch(cmd) {
+    switch (cmd) {
     case CHR_IOCTL_PP_READ_DATA:
-        if (ioctl(fd, PPIGDATA, &b) < 0)
+        if (ioctl(drv->fd, PPIGDATA, &b) < 0) {
             return -ENOTSUP;
+        }
         *(uint8_t *)arg = b;
         break;
     case CHR_IOCTL_PP_WRITE_DATA:
         b = *(uint8_t *)arg;
-        if (ioctl(fd, PPISDATA, &b) < 0)
+        if (ioctl(drv->fd, PPISDATA, &b) < 0) {
             return -ENOTSUP;
+        }
         break;
     case CHR_IOCTL_PP_READ_CONTROL:
-        if (ioctl(fd, PPIGCTRL, &b) < 0)
+        if (ioctl(drv->fd, PPIGCTRL, &b) < 0) {
             return -ENOTSUP;
+        }
         *(uint8_t *)arg = b;
         break;
     case CHR_IOCTL_PP_WRITE_CONTROL:
         b = *(uint8_t *)arg;
-        if (ioctl(fd, PPISCTRL, &b) < 0)
+        if (ioctl(drv->fd, PPISCTRL, &b) < 0) {
             return -ENOTSUP;
+        }
         break;
     case CHR_IOCTL_PP_READ_STATUS:
-        if (ioctl(fd, PPIGSTATUS, &b) < 0)
+        if (ioctl(drv->fd, PPIGSTATUS, &b) < 0) {
             return -ENOTSUP;
+        }
         *(uint8_t *)arg = b;
         break;
     default:
@@ -2088,12 +2093,14 @@ static CharDriverState *qemu_chr_open_pp_fd(const CharDriver *driver,
                                             Error **errp)
 {
     CharDriverState *chr;
+    ParallelCharDriver *drv;
 
     chr = qemu_chr_alloc(driver, backend, errp);
     if (!chr) {
         return NULL;
     }
-    chr->opaque = (void *)(intptr_t)fd;
+    drv = (ParallelCharDriver *)chr;
+    drv->fd = fd;
     *be_opened = false;
     return chr;
 }
@@ -2104,6 +2111,7 @@ static CharDriverState *qemu_chr_open_pp_fd(const CharDriver *driver,
 #define HAVE_CHARDEV_SERIAL 1
 
 typedef struct {
+    CharDriverState parent;
     int max_size;
     HANDLE hcom, hrecv, hsend;
     OVERLAPPED orecv;
@@ -2115,6 +2123,7 @@ typedef struct {
 } WinCharState;
 
 typedef struct {
+    CharDriverState parent;
     HANDLE  hStdIn;
     HANDLE  hInputReadyEvent;
     HANDLE  hInputDoneEvent;
@@ -2132,7 +2141,7 @@ static int win_chr_pipe_poll(void *opaque);
 
 static void win_chr_free(CharDriverState *chr)
 {
-    WinCharState *s = chr->opaque;
+    WinCharState *s = (WinCharState *)chr;
 
     if (s->hsend) {
         CloseHandle(s->hsend);
@@ -2156,7 +2165,7 @@ static void win_chr_free(CharDriverState *chr)
 
 static int win_chr_init(CharDriverState *chr, const char *filename, Error **errp)
 {
-    WinCharState *s = chr->opaque;
+    WinCharState *s = (WinCharState *)chr;
     COMMCONFIG comcfg;
     COMMTIMEOUTS cto = { 0, 0, 0, 0, 0};
     COMSTAT comstat;
@@ -2224,7 +2233,7 @@ static int win_chr_init(CharDriverState *chr, const char *filename, Error **errp
 /* Called with chr_write_lock held.  */
 static int win_chr_write(CharDriverState *chr, const uint8_t *buf, int len1)
 {
-    WinCharState *s = chr->opaque;
+    WinCharState *s = (WinCharState *)chr;
     DWORD len, ret, size, err;
 
     len = len1;
@@ -2258,7 +2267,7 @@ static int win_chr_write(CharDriverState *chr, const uint8_t *buf, int len1)
 
 static int win_chr_read_poll(CharDriverState *chr)
 {
-    WinCharState *s = chr->opaque;
+    WinCharState *s = (WinCharState *)chr;
 
     s->max_size = qemu_chr_be_can_write(chr);
     return s->max_size;
@@ -2266,7 +2275,7 @@ static int win_chr_read_poll(CharDriverState *chr)
 
 static void win_chr_readfile(CharDriverState *chr)
 {
-    WinCharState *s = chr->opaque;
+    WinCharState *s = (WinCharState *)chr;
     int ret, err;
     uint8_t buf[READ_BUF_LEN];
     DWORD size;
@@ -2288,7 +2297,7 @@ static void win_chr_readfile(CharDriverState *chr)
 
 static void win_chr_read(CharDriverState *chr)
 {
-    WinCharState *s = chr->opaque;
+    WinCharState *s = (WinCharState *)chr;
 
     if (s->len > s->max_size)
         s->len = s->max_size;
@@ -2301,7 +2310,7 @@ static void win_chr_read(CharDriverState *chr)
 static int win_chr_poll(void *opaque)
 {
     CharDriverState *chr = opaque;
-    WinCharState *s = chr->opaque;
+    WinCharState *s = opaque;
     COMSTAT status;
     DWORD comerr;
 
@@ -2318,7 +2327,7 @@ static int win_chr_poll(void *opaque)
 static int win_chr_pipe_poll(void *opaque)
 {
     CharDriverState *chr = opaque;
-    WinCharState *s = chr->opaque;
+    WinCharState *s = opaque;
     DWORD size;
 
     PeekNamedPipe(s->hcom, NULL, 0, NULL, &size, NULL);
@@ -2334,7 +2343,7 @@ static int win_chr_pipe_poll(void *opaque)
 static int win_chr_pipe_init(CharDriverState *chr, const char *filename,
                              Error **errp)
 {
-    WinCharState *s = chr->opaque;
+    WinCharState *s = (WinCharState *)chr;
     OVERLAPPED ov;
     int ret;
     DWORD size;
@@ -2406,18 +2415,14 @@ static CharDriverState *qemu_chr_open_pipe(const CharDriver *driver,
     ChardevHostdev *opts = backend->u.pipe.data;
     const char *filename = opts->device;
     CharDriverState *chr;
-    WinCharState *s;
     ChardevCommon *common = qapi_ChardevHostdev_base(opts);
 
     chr = qemu_chr_alloc(driver, common, errp);
     if (!chr) {
         return NULL;
     }
-    s = g_new0(WinCharState, 1);
-    chr->opaque = s;
 
     if (win_chr_pipe_init(chr, filename, errp) < 0) {
-        g_free(s);
         qemu_chr_free_common(chr);
         return NULL;
     }
@@ -2436,9 +2441,8 @@ static CharDriverState *qemu_chr_open_win_file(const CharDriver *driver,
     if (!chr) {
         return NULL;
     }
-    s = g_new0(WinCharState, 1);
+    s = (WinCharState *)chr;
     s->hcom = fd_out;
-    chr->opaque = s;
     return chr;
 }
 
@@ -2456,6 +2460,7 @@ static CharDriverState *qemu_chr_open_win_con(const CharDriver *driver,
 }
 
 static const CharDriver console_driver = {
+    .instance_size = sizeof(WinCharState),
     .kind = CHARDEV_BACKEND_KIND_CONSOLE,
     .create = qemu_chr_open_win_con,
     .chr_write = win_chr_write,
@@ -2483,7 +2488,7 @@ static int win_stdio_write(CharDriverState *chr, const uint8_t *buf, int len)
 static void win_stdio_wait_func(void *opaque)
 {
     CharDriverState   *chr   = opaque;
-    WinStdioCharState *stdio = chr->opaque;
+    WinStdioCharState *stdio = opaque;
     INPUT_RECORD       buf[4];
     int                ret;
     DWORD              dwSize;
@@ -2516,8 +2521,7 @@ static void win_stdio_wait_func(void *opaque)
 
 static DWORD WINAPI win_stdio_thread(LPVOID param)
 {
-    CharDriverState   *chr   = param;
-    WinStdioCharState *stdio = chr->opaque;
+    WinStdioCharState *stdio = param;
     int                ret;
     DWORD              dwSize;
 
@@ -2556,7 +2560,7 @@ static DWORD WINAPI win_stdio_thread(LPVOID param)
 static void win_stdio_thread_wait_func(void *opaque)
 {
     CharDriverState   *chr   = opaque;
-    WinStdioCharState *stdio = chr->opaque;
+    WinStdioCharState *stdio = opaque;
 
     if (qemu_chr_be_can_write(chr)) {
         qemu_chr_be_write(chr, &stdio->win_stdio_buf, 1);
@@ -2567,7 +2571,7 @@ static void win_stdio_thread_wait_func(void *opaque)
 
 static void qemu_chr_set_echo_win_stdio(CharDriverState *chr, bool echo)
 {
-    WinStdioCharState *stdio  = chr->opaque;
+    WinStdioCharState *stdio  = (WinStdioCharState *)chr;
     DWORD              dwMode = 0;
 
     GetConsoleMode(stdio->hStdIn, &dwMode);
@@ -2581,7 +2585,7 @@ static void qemu_chr_set_echo_win_stdio(CharDriverState *chr, bool echo)
 
 static void win_stdio_free(CharDriverState *chr)
 {
-    WinStdioCharState *stdio = chr->opaque;
+    WinStdioCharState *stdio = (WinStdioCharState *)chr;
 
     if (stdio->hInputReadyEvent != INVALID_HANDLE_VALUE) {
         CloseHandle(stdio->hInputReadyEvent);
@@ -2592,8 +2596,6 @@ static void win_stdio_free(CharDriverState *chr)
     if (stdio->hInputThread != INVALID_HANDLE_VALUE) {
         TerminateThread(stdio->hInputThread, 0);
     }
-
-    g_free(chr->opaque);
 }
 
 static CharDriverState *qemu_chr_open_stdio(const CharDriver *driver,
@@ -2613,7 +2615,7 @@ static CharDriverState *qemu_chr_open_stdio(const CharDriver *driver,
     if (!chr) {
         return NULL;
     }
-    stdio = g_new0(WinStdioCharState, 1);
+    stdio = (WinStdioCharState *)chr;
 
     stdio->hStdIn = GetStdHandle(STD_INPUT_HANDLE);
     if (stdio->hStdIn == INVALID_HANDLE_VALUE) {
@@ -2623,8 +2625,6 @@ static CharDriverState *qemu_chr_open_stdio(const CharDriver *driver,
 
     is_console = GetConsoleMode(stdio->hStdIn, &dwMode) != 0;
 
-    chr->opaque    = stdio;
-
     if (is_console) {
         if (qemu_add_wait_object(stdio->hStdIn,
                                  win_stdio_wait_func, chr)) {
@@ -2684,6 +2684,7 @@ err1:
 /* UDP Net console */
 
 typedef struct {
+    CharDriverState parent;
     QIOChannel *ioc;
     uint8_t buf[READ_BUF_LEN];
     int bufcnt;
@@ -2694,7 +2695,7 @@ typedef struct {
 /* Called with chr_write_lock held.  */
 static int udp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
 {
-    NetCharDriver *s = chr->opaque;
+    NetCharDriver *s = (NetCharDriver *)chr;
 
     return qio_channel_write(
         s->ioc, (const char *)buf, len, NULL);
@@ -2703,7 +2704,7 @@ static int udp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
 static int udp_chr_read_poll(void *opaque)
 {
     CharDriverState *chr = opaque;
-    NetCharDriver *s = chr->opaque;
+    NetCharDriver *s = opaque;
 
     s->max_size = qemu_chr_be_can_write(chr);
 
@@ -2721,7 +2722,7 @@ static int udp_chr_read_poll(void *opaque)
 static gboolean udp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
 {
     CharDriverState *chr = opaque;
-    NetCharDriver *s = chr->opaque;
+    NetCharDriver *s = opaque;
     ssize_t ret;
 
     if (s->max_size == 0) {
@@ -2748,7 +2749,7 @@ static gboolean udp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
 static void udp_chr_update_read_handler(CharDriverState *chr,
                                         GMainContext *context)
 {
-    NetCharDriver *s = chr->opaque;
+    NetCharDriver *s = (NetCharDriver *)chr;
 
     remove_fd_in_watch(chr);
     if (s->ioc) {
@@ -2761,13 +2762,12 @@ static void udp_chr_update_read_handler(CharDriverState *chr,
 
 static void udp_chr_free(CharDriverState *chr)
 {
-    NetCharDriver *s = chr->opaque;
+    NetCharDriver *s = (NetCharDriver *)chr;
 
     remove_fd_in_watch(chr);
     if (s->ioc) {
         object_unref(OBJECT(s->ioc));
     }
-    g_free(s);
     qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
 }
 
@@ -2775,6 +2775,7 @@ static void udp_chr_free(CharDriverState *chr)
 /* TCP Net console */
 
 typedef struct {
+    CharDriverState parent;
     QIOChannel *ioc; /* Client I/O channel */
     QIOChannelSocket *sioc; /* Client master channel */
     QIOChannelSocket *listen_ioc;
@@ -2803,8 +2804,9 @@ static gboolean socket_reconnect_timeout(gpointer opaque);
 
 static void qemu_chr_socket_restart_timer(CharDriverState *chr)
 {
-    TCPCharDriver *s = chr->opaque;
+    TCPCharDriver *s = (TCPCharDriver *)chr;
     char *name;
+
     assert(s->connected == 0);
     s->reconnect_timer = g_timeout_add_seconds(s->reconnect_time,
                                                socket_reconnect_timeout, chr);
@@ -2816,7 +2818,7 @@ static void qemu_chr_socket_restart_timer(CharDriverState *chr)
 static void check_report_connect_error(CharDriverState *chr,
                                        Error *err)
 {
-    TCPCharDriver *s = chr->opaque;
+    TCPCharDriver *s = (TCPCharDriver *)chr;
 
     if (!s->connect_err_reported) {
         error_report("Unable to connect character device %s: %s",
@@ -2833,7 +2835,8 @@ static gboolean tcp_chr_accept(QIOChannel *chan,
 /* Called with chr_write_lock held.  */
 static int tcp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
 {
-    TCPCharDriver *s = chr->opaque;
+    TCPCharDriver *s = (TCPCharDriver *)chr;
+
     if (s->connected) {
         int ret =  io_channel_send_full(s->ioc, buf, len,
                                         s->write_msgfds,
@@ -2856,7 +2859,7 @@ static int tcp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
 static int tcp_chr_read_poll(void *opaque)
 {
     CharDriverState *chr = opaque;
-    TCPCharDriver *s = chr->opaque;
+    TCPCharDriver *s = opaque;
     if (!s->connected)
         return 0;
     s->max_size = qemu_chr_be_can_write(chr);
@@ -2915,7 +2918,8 @@ static void tcp_chr_process_IAC_bytes(CharDriverState *chr,
 
 static int tcp_get_msgfds(CharDriverState *chr, int *fds, int num)
 {
-    TCPCharDriver *s = chr->opaque;
+    TCPCharDriver *s = (TCPCharDriver *)chr;
+
     int to_copy = (s->read_msgfds_num < num) ? s->read_msgfds_num : num;
 
     assert(num <= TCP_MAX_FDS);
@@ -2940,7 +2944,7 @@ static int tcp_get_msgfds(CharDriverState *chr, int *fds, int num)
 
 static int tcp_set_msgfds(CharDriverState *chr, int *fds, int num)
 {
-    TCPCharDriver *s = chr->opaque;
+    TCPCharDriver *s = (TCPCharDriver *)chr;
 
     /* clear old pending fd array */
     g_free(s->write_msgfds);
@@ -2965,7 +2969,7 @@ static int tcp_set_msgfds(CharDriverState *chr, int *fds, int num)
 
 static ssize_t tcp_chr_recv(CharDriverState *chr, char *buf, size_t len)
 {
-    TCPCharDriver *s = chr->opaque;
+    TCPCharDriver *s = (TCPCharDriver *)chr;
     struct iovec iov = { .iov_base = buf, .iov_len = len };
     int ret;
     size_t i;
@@ -3022,13 +3026,13 @@ static ssize_t tcp_chr_recv(CharDriverState *chr, char *buf, size_t len)
 
 static GSource *tcp_chr_add_watch(CharDriverState *chr, GIOCondition cond)
 {
-    TCPCharDriver *s = chr->opaque;
+    TCPCharDriver *s = (TCPCharDriver *)chr;
     return qio_channel_create_watch(s->ioc, cond);
 }
 
 static void tcp_chr_free_connection(CharDriverState *chr)
 {
-    TCPCharDriver *s = chr->opaque;
+    TCPCharDriver *s = (TCPCharDriver *)chr;
     int i;
 
     if (!s->connected) {
@@ -3057,7 +3061,7 @@ static void tcp_chr_free_connection(CharDriverState *chr)
 
 static void tcp_chr_disconnect(CharDriverState *chr)
 {
-    TCPCharDriver *s = chr->opaque;
+    TCPCharDriver *s = (TCPCharDriver *)chr;
 
     if (!s->connected) {
         return;
@@ -3080,7 +3084,7 @@ static void tcp_chr_disconnect(CharDriverState *chr)
 static gboolean tcp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
 {
     CharDriverState *chr = opaque;
-    TCPCharDriver *s = chr->opaque;
+    TCPCharDriver *s = opaque;
     uint8_t buf[READ_BUF_LEN];
     int len, size;
 
@@ -3106,7 +3110,7 @@ static gboolean tcp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
 
 static int tcp_chr_sync_read(CharDriverState *chr, const uint8_t *buf, int len)
 {
-    TCPCharDriver *s = chr->opaque;
+    TCPCharDriver *s = (TCPCharDriver *)chr;
     int size;
 
     if (!s->connected) {
@@ -3125,7 +3129,7 @@ static int tcp_chr_sync_read(CharDriverState *chr, const uint8_t *buf, int len)
 static void tcp_chr_connect(void *opaque)
 {
     CharDriverState *chr = opaque;
-    TCPCharDriver *s = chr->opaque;
+    TCPCharDriver *s = opaque;
 
     g_free(chr->filename);
     chr->filename = sockaddr_to_str(
@@ -3146,7 +3150,7 @@ static void tcp_chr_connect(void *opaque)
 static void tcp_chr_update_read_handler(CharDriverState *chr,
                                         GMainContext *context)
 {
-    TCPCharDriver *s = chr->opaque;
+    TCPCharDriver *s = (TCPCharDriver *)chr;
 
     if (!s->connected) {
         return;
@@ -3197,7 +3201,7 @@ static gboolean tcp_chr_telnet_init_io(QIOChannel *ioc,
 
 static void tcp_chr_telnet_init(CharDriverState *chr)
 {
-    TCPCharDriver *s = chr->opaque;
+    TCPCharDriver *s = (TCPCharDriver *)chr;
     TCPCharDriverTelnetInit *init =
         g_new0(TCPCharDriverTelnetInit, 1);
     size_t n = 0;
@@ -3232,7 +3236,7 @@ static void tcp_chr_tls_handshake(QIOTask *task,
                                   gpointer user_data)
 {
     CharDriverState *chr = user_data;
-    TCPCharDriver *s = chr->opaque;
+    TCPCharDriver *s = user_data;
 
     if (qio_task_propagate_error(task, NULL)) {
         tcp_chr_disconnect(chr);
@@ -3248,7 +3252,7 @@ static void tcp_chr_tls_handshake(QIOTask *task,
 
 static void tcp_chr_tls_init(CharDriverState *chr)
 {
-    TCPCharDriver *s = chr->opaque;
+    TCPCharDriver *s = (TCPCharDriver *)chr;
     QIOChannelTLS *tioc;
     Error *err = NULL;
     gchar *name;
@@ -3287,7 +3291,7 @@ static void tcp_chr_tls_init(CharDriverState *chr)
 static void tcp_chr_set_client_ioc_name(CharDriverState *chr,
                                         QIOChannelSocket *sioc)
 {
-    TCPCharDriver *s = chr->opaque;
+    TCPCharDriver *s = (TCPCharDriver *)chr;
     char *name;
     name = g_strdup_printf("chardev-tcp-%s-%s",
                            s->is_listen ? "server" : "client",
@@ -3299,7 +3303,8 @@ static void tcp_chr_set_client_ioc_name(CharDriverState *chr,
 
 static int tcp_chr_new_client(CharDriverState *chr, QIOChannelSocket *sioc)
 {
-    TCPCharDriver *s = chr->opaque;
+    TCPCharDriver *s = (TCPCharDriver *)chr;
+
     if (s->ioc != NULL) {
 	return -1;
     }
@@ -3370,7 +3375,7 @@ static gboolean tcp_chr_accept(QIOChannel *channel,
 
 static int tcp_chr_wait_connected(CharDriverState *chr, Error **errp)
 {
-    TCPCharDriver *s = chr->opaque;
+    TCPCharDriver *s = (TCPCharDriver *)chr;
     QIOChannelSocket *sioc;
 
     /* It can't wait on s->connected, since it is set asynchronously
@@ -3418,7 +3423,7 @@ int qemu_chr_fe_wait_connected(CharBackend *be, Error **errp)
 
 static void tcp_chr_free(CharDriverState *chr)
 {
-    TCPCharDriver *s = chr->opaque;
+    TCPCharDriver *s = (TCPCharDriver *)chr;
 
     tcp_chr_free_connection(chr);
 
@@ -3437,7 +3442,7 @@ static void tcp_chr_free(CharDriverState *chr)
     if (s->tls_creds) {
         object_unref(OBJECT(s->tls_creds));
     }
-    g_free(s);
+
     qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
 }
 
@@ -3446,7 +3451,7 @@ static void qemu_chr_socket_connected(QIOTask *task, void *opaque)
 {
     QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(qio_task_get_source(task));
     CharDriverState *chr = opaque;
-    TCPCharDriver *s = chr->opaque;
+    TCPCharDriver *s = (TCPCharDriver *)chr;
     Error *err = NULL;
 
     if (qio_task_propagate_error(task, &err)) {
@@ -3467,6 +3472,7 @@ static void qemu_chr_socket_connected(QIOTask *task, void *opaque)
 /* Ring buffer chardev */
 
 typedef struct {
+    CharDriverState parent;
     size_t size;
     size_t prod;
     size_t cons;
@@ -3475,7 +3481,7 @@ typedef struct {
 
 static size_t ringbuf_count(const CharDriverState *chr)
 {
-    const RingBufCharDriver *d = chr->opaque;
+    const RingBufCharDriver *d = (RingBufCharDriver *)chr;
 
     return d->prod - d->cons;
 }
@@ -3483,7 +3489,7 @@ static size_t ringbuf_count(const CharDriverState *chr)
 /* Called with chr_write_lock held.  */
 static int ringbuf_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
 {
-    RingBufCharDriver *d = chr->opaque;
+    RingBufCharDriver *d = (RingBufCharDriver *)chr;
     int i;
 
     if (!buf || (len < 0)) {
@@ -3502,7 +3508,7 @@ static int ringbuf_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
 
 static int ringbuf_chr_read(CharDriverState *chr, uint8_t *buf, int len)
 {
-    RingBufCharDriver *d = chr->opaque;
+    RingBufCharDriver *d = (RingBufCharDriver *)chr;
     int i;
 
     qemu_mutex_lock(&chr->chr_write_lock);
@@ -3516,11 +3522,9 @@ static int ringbuf_chr_read(CharDriverState *chr, uint8_t *buf, int len)
 
 static void ringbuf_chr_free(struct CharDriverState *chr)
 {
-    RingBufCharDriver *d = chr->opaque;
+    RingBufCharDriver *d = (RingBufCharDriver *)chr;
 
     g_free(d->cbuf);
-    g_free(d);
-    chr->opaque = NULL;
 }
 
 static CharDriverState *qemu_chr_open_ringbuf(const CharDriver *driver,
@@ -3539,7 +3543,7 @@ static CharDriverState *qemu_chr_open_ringbuf(const CharDriver *driver,
     if (!chr) {
         return NULL;
     }
-    d = g_malloc(sizeof(*d));
+    d = (RingBufCharDriver *)chr;
 
     d->size = opts->has_size ? opts->size : 65536;
 
@@ -3553,12 +3557,9 @@ static CharDriverState *qemu_chr_open_ringbuf(const CharDriver *driver,
     d->cons = 0;
     d->cbuf = g_malloc0(d->size);
 
-    chr->opaque = d;
-
     return chr;
 
 fail:
-    g_free(d);
     qemu_chr_free_common(chr);
     return NULL;
 }
@@ -3849,10 +3850,12 @@ static const CharDriver stdio_driver = {
     .parse = qemu_chr_parse_stdio,
     .create = qemu_chr_open_stdio,
 #ifdef _WIN32
+    sizeof(WinStdioCharState),
     .chr_write = win_stdio_write,
     .chr_set_echo = qemu_chr_set_echo_win_stdio,
     .chr_free = win_stdio_free,
 #else
+    sizeof(FDCharDriver),
     .chr_add_watch = fd_chr_add_watch,
     .chr_write = fd_chr_write,
     .chr_update_read_handler = fd_chr_update_read_handler,
@@ -3915,9 +3918,11 @@ static const CharDriver pipe_driver = {
     .parse = qemu_chr_parse_pipe,
     .create = qemu_chr_open_pipe,
 #ifdef _WIN32
+    sizeof(WinCharState),
     .chr_write = win_chr_write,
     .chr_free = win_chr_free,
 #else
+    sizeof(FDCharDriver),
     .chr_add_watch = fd_chr_add_watch,
     .chr_write = fd_chr_write,
     .chr_update_read_handler = fd_chr_update_read_handler,
@@ -3942,6 +3947,7 @@ static void qemu_chr_parse_ringbuf(QemuOpts *opts, ChardevBackend *backend,
 }
 
 static const CharDriver ringbuf_driver = {
+    .instance_size = sizeof(RingBufCharDriver),
     .kind = CHARDEV_BACKEND_KIND_RINGBUF,
     .parse = qemu_chr_parse_ringbuf,
     .create = qemu_chr_open_ringbuf,
@@ -3951,6 +3957,7 @@ static const CharDriver ringbuf_driver = {
 
 /* Bug-compatibility: */
 static const CharDriver memory_driver = {
+    .instance_size = sizeof(RingBufCharDriver),
     .kind = CHARDEV_BACKEND_KIND_MEMORY,
     .parse = qemu_chr_parse_ringbuf,
     .create = qemu_chr_open_ringbuf,
@@ -3974,6 +3981,7 @@ static void qemu_chr_parse_mux(QemuOpts *opts, ChardevBackend *backend,
 }
 
 static const CharDriver mux_driver = {
+    .instance_size = sizeof(MuxDriver),
     .kind = CHARDEV_BACKEND_KIND_MUX,
     .parse = qemu_chr_parse_mux,
     .create = qemu_chr_open_mux,
@@ -4558,17 +4566,13 @@ static CharDriverState *qmp_chardev_open_serial(const CharDriver *driver,
     ChardevHostdev *serial = backend->u.serial.data;
     ChardevCommon *common = qapi_ChardevHostdev_base(serial);
     CharDriverState *chr;
-    WinCharState *s;
 
     chr = qemu_chr_alloc(driver, common, errp);
     if (!chr) {
         return NULL;
     }
 
-    s = g_new0(WinCharState, 1);
-    chr->opaque = s;
     if (win_chr_init(chr, serial->device, errp) < 0) {
-        g_free(s);
         qemu_chr_free_common(chr);
         return NULL;
     }
@@ -4668,6 +4672,7 @@ static CharDriverState *qmp_chardev_open_parallel(const CharDriver *driver,
 }
 
 static const CharDriver parallel_driver = {
+    .instance_size = sizeof(ParallelCharDriver),
     .kind = CHARDEV_BACKEND_KIND_PARALLEL,
     .alias = "parport",
     .parse = qemu_chr_parse_parallel,
@@ -4691,9 +4696,11 @@ static const CharDriver file_driver = {
     .parse = qemu_chr_parse_file_out,
     .create = qmp_chardev_open_file,
 #ifdef _WIN32
+    sizeof(WinCharState),
     .chr_write = win_chr_write,
     /* FIXME: no chr_free */
 #else
+    sizeof(FDCharDriver),
     .chr_add_watch = fd_chr_add_watch,
     .chr_write = fd_chr_write,
     .chr_update_read_handler = fd_chr_update_read_handler,
@@ -4708,9 +4715,11 @@ static const CharDriver serial_driver = {
     .parse = qemu_chr_parse_serial,
     .create = qmp_chardev_open_serial,
 #ifdef _WIN32
+    sizeof(WinCharState),
     .chr_write = win_chr_write,
     .chr_free = win_chr_free,
 #else
+    sizeof(FDCharDriver),
     .chr_add_watch = fd_chr_add_watch,
     .chr_write = fd_chr_write,
     .chr_update_read_handler = fd_chr_update_read_handler,
@@ -4723,7 +4732,7 @@ static const CharDriver serial_driver = {
 static gboolean socket_reconnect_timeout(gpointer opaque)
 {
     CharDriverState *chr = opaque;
-    TCPCharDriver *s = chr->opaque;
+    TCPCharDriver *s = opaque;
     QIOChannelSocket *sioc;
 
     s->reconnect_timer = 0;
@@ -4764,7 +4773,7 @@ static CharDriverState *qmp_chardev_open_socket(const CharDriver *driver,
     if (!chr) {
         return NULL;
     }
-    s = g_new0(TCPCharDriver, 1);
+    s = (TCPCharDriver *)chr;
 
     s->is_unix = addr->type == SOCKET_ADDRESS_KIND_UNIX;
     s->is_listen = is_listen;
@@ -4810,7 +4819,6 @@ static CharDriverState *qmp_chardev_open_socket(const CharDriver *driver,
         qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_FD_PASS);
     }
 
-    chr->opaque = s;
     /* be isn't opened until we get a connection */
     *be_opened = false;
 
@@ -4867,12 +4875,12 @@ static CharDriverState *qmp_chardev_open_socket(const CharDriver *driver,
     if (s->tls_creds) {
         object_unref(OBJECT(s->tls_creds));
     }
-    g_free(s);
     qemu_chr_free_common(chr);
     return NULL;
 }
 
 static const CharDriver socket_driver = {
+    .instance_size = sizeof(TCPCharDriver),
     .kind = CHARDEV_BACKEND_KIND_SOCKET,
     .parse = qemu_chr_parse_socket,
     .create = qmp_chardev_open_socket,
@@ -4918,9 +4926,8 @@ static CharDriverState *qmp_chardev_open_udp(const CharDriver *driver,
     qio_channel_set_name(QIO_CHANNEL(sioc), name);
     g_free(name);
 
-    s = g_new0(NetCharDriver, 1);
+    s = (NetCharDriver *)chr;
     s->ioc = QIO_CHANNEL(sioc);
-    chr->opaque = s;
     /* be isn't opened until we get a connection */
     *be_opened = false;
 
@@ -4928,6 +4935,7 @@ static CharDriverState *qmp_chardev_open_udp(const CharDriver *driver,
 }
 
 static const CharDriver udp_driver = {
+    .instance_size = sizeof(NetCharDriver),
     .kind = CHARDEV_BACKEND_KIND_UDP,
     .parse = qemu_chr_parse_udp,
     .create = qmp_chardev_open_udp,
diff --git a/spice-qemu-char.c b/spice-qemu-char.c
index c7eb306..1aed451 100644
--- a/spice-qemu-char.c
+++ b/spice-qemu-char.c
@@ -7,7 +7,8 @@
 
 
 typedef struct SpiceCharDriver {
-    CharDriverState*      chr;
+    CharDriverState       parent;
+
     SpiceCharDeviceInstance     sin;
     bool                  active;
     bool                  blocked;
@@ -27,17 +28,18 @@ static QLIST_HEAD(, SpiceCharDriver) spice_chars =
 static int vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len)
 {
     SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin);
+    CharDriverState *chr = (CharDriverState *)scd;
     ssize_t out = 0;
     ssize_t last_out;
     uint8_t* p = (uint8_t*)buf;
 
     while (len > 0) {
-        int can_write = qemu_chr_be_can_write(scd->chr);
+        int can_write = qemu_chr_be_can_write(chr);
         last_out = MIN(len, can_write);
         if (last_out <= 0) {
             break;
         }
-        qemu_chr_be_write(scd->chr, p, last_out);
+        qemu_chr_be_write(chr, p, last_out);
         out += last_out;
         len -= last_out;
         p += last_out;
@@ -70,6 +72,7 @@ static int vmc_read(SpiceCharDeviceInstance *sin, uint8_t *buf, int len)
 static void vmc_event(SpiceCharDeviceInstance *sin, uint8_t event)
 {
     SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin);
+    CharDriverState *chr = (CharDriverState *)scd;
     int chr_event;
 
     switch (event) {
@@ -81,20 +84,21 @@ static void vmc_event(SpiceCharDeviceInstance *sin, uint8_t event)
     }
 
     trace_spice_vmc_event(chr_event);
-    qemu_chr_be_event(scd->chr, chr_event);
+    qemu_chr_be_event(chr, chr_event);
 }
 #endif
 
 static void vmc_state(SpiceCharDeviceInstance *sin, int connected)
 {
     SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin);
+    CharDriverState *chr = (CharDriverState *)scd;
 
-    if ((scd->chr->be_open && connected) ||
-        (!scd->chr->be_open && !connected)) {
+    if ((chr->be_open && connected) ||
+        (!chr->be_open && !connected)) {
         return;
     }
 
-    qemu_chr_be_event(scd->chr,
+    qemu_chr_be_event(chr,
                       connected ? CHR_EVENT_OPENED : CHR_EVENT_CLOSED);
 }
 
@@ -168,7 +172,7 @@ static GSourceFuncs SpiceCharSourceFuncs = {
 
 static GSource *spice_chr_add_watch(CharDriverState *chr, GIOCondition cond)
 {
-    SpiceCharDriver *scd = chr->opaque;
+    SpiceCharDriver *scd = (SpiceCharDriver *)chr;
     SpiceCharSource *src;
 
     assert(cond & G_IO_OUT);
@@ -182,7 +186,7 @@ static GSource *spice_chr_add_watch(CharDriverState *chr, GIOCondition cond)
 
 static int spice_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
 {
-    SpiceCharDriver *s = chr->opaque;
+    SpiceCharDriver *s = (SpiceCharDriver *)chr;
     int read_bytes;
 
     assert(s->datalen == 0);
@@ -201,7 +205,7 @@ static int spice_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
 
 static void spice_chr_free(struct CharDriverState *chr)
 {
-    SpiceCharDriver *s = chr->opaque;
+    SpiceCharDriver *s = (SpiceCharDriver *)chr;
 
     vmc_unregister_interface(s);
     QLIST_REMOVE(s, next);
@@ -210,12 +214,11 @@ static void spice_chr_free(struct CharDriverState *chr)
 #if SPICE_SERVER_VERSION >= 0x000c02
     g_free((char *)s->sin.portname);
 #endif
-    g_free(s);
 }
 
 static void spice_vmc_set_fe_open(struct CharDriverState *chr, int fe_open)
 {
-    SpiceCharDriver *s = chr->opaque;
+    SpiceCharDriver *s = (SpiceCharDriver *)chr;
     if (fe_open) {
         vmc_register_interface(s);
     } else {
@@ -226,7 +229,7 @@ static void spice_vmc_set_fe_open(struct CharDriverState *chr, int fe_open)
 static void spice_port_set_fe_open(struct CharDriverState *chr, int fe_open)
 {
 #if SPICE_SERVER_VERSION >= 0x000c02
-    SpiceCharDriver *s = chr->opaque;
+    SpiceCharDriver *s = (SpiceCharDriver *)chr;
 
     if (fe_open) {
         spice_server_port_event(&s->sin, SPICE_PORT_EVENT_OPENED);
@@ -255,7 +258,7 @@ static void print_allowed_subtypes(void)
 
 static void spice_chr_accept_input(struct CharDriverState *chr)
 {
-    SpiceCharDriver *s = chr->opaque;
+    SpiceCharDriver *s = (SpiceCharDriver *)chr;
 
     spice_server_char_device_wakeup(&s->sin);
 }
@@ -272,11 +275,9 @@ static CharDriverState *chr_open(const CharDriver *driver,
     if (!chr) {
         return NULL;
     }
-    s = g_malloc0(sizeof(SpiceCharDriver));
-    s->chr = chr;
+    s = (SpiceCharDriver *)chr;
     s->active = false;
     s->sin.subtype = g_strdup(subtype);
-    chr->opaque = s;
 
     QLIST_INSERT_HEAD(&spice_chars, s, next);
 
@@ -334,7 +335,7 @@ static CharDriverState *qemu_chr_open_spice_port(const CharDriver *driver,
         return NULL;
     }
     *be_opened = false;
-    s = chr->opaque;
+    s = (SpiceCharDriver *)chr;
     s->sin.portname = g_strdup(name);
 
     return chr;
@@ -386,6 +387,7 @@ static void qemu_chr_parse_spice_port(QemuOpts *opts, ChardevBackend *backend,
 static void register_types(void)
 {
     static const CharDriver vmc_driver = {
+        .instance_size = sizeof(SpiceCharDriver),
         .kind = CHARDEV_BACKEND_KIND_SPICEVMC,
         .parse = qemu_chr_parse_spice_vmc,
         .create = qemu_chr_open_spice_vmc,
@@ -396,6 +398,7 @@ static void register_types(void)
         .chr_free = spice_chr_free,
     };
     static const CharDriver port_driver = {
+        .instance_size = sizeof(SpiceCharDriver),
         .kind = CHARDEV_BACKEND_KIND_SPICEPORT,
         .parse = qemu_chr_parse_spice_port,
         .create = qemu_chr_open_spice_port,
diff --git a/ui/console.c b/ui/console.c
index f48ba26..495d5a2 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -1046,9 +1046,15 @@ void console_select(unsigned int index)
     }
 }
 
+typedef struct VCDriverState {
+    CharDriverState parent;
+    QemuConsole *console;
+} VCDriverState;
+
 static int console_puts(CharDriverState *chr, const uint8_t *buf, int len)
 {
-    QemuConsole *s = chr->opaque;
+    VCDriverState *drv = (VCDriverState *)chr;
+    QemuConsole *s = drv->console;
     int i;
 
     if (!s->ds) {
@@ -1958,7 +1964,8 @@ int qemu_console_get_height(QemuConsole *con, int fallback)
 
 static void text_console_set_echo(CharDriverState *chr, bool echo)
 {
-    QemuConsole *s = chr->opaque;
+    VCDriverState *drv = (VCDriverState *)chr;
+    QemuConsole *s = drv->console;
 
     s->echo = echo;
 }
@@ -1998,12 +2005,11 @@ static const GraphicHwOps text_console_ops = {
 
 static void text_console_do_init(CharDriverState *chr, DisplayState *ds)
 {
-    QemuConsole *s;
+    VCDriverState *drv = (VCDriverState *)chr;
+    QemuConsole *s = drv->console;
     int g_width = 80 * FONT_WIDTH;
     int g_height = 24 * FONT_HEIGHT;
 
-    s = chr->opaque;
-
     s->out_fifo.buf = s->out_fifo_buf;
     s->out_fifo.buf_size = sizeof(s->out_fifo_buf);
     s->kbd_timer = timer_new_ms(QEMU_CLOCK_REALTIME, kbd_send_chars, s);
@@ -2056,6 +2062,7 @@ static CharDriverState *text_console_init(ChardevVC *vc, Error **errp)
 {
     ChardevCommon *common = qapi_ChardevVC_base(vc);
     CharDriverState *chr;
+    VCDriverState *drv;
     QemuConsole *s;
     unsigned width = 0;
     unsigned height = 0;
@@ -2092,7 +2099,8 @@ static CharDriverState *text_console_init(ChardevVC *vc, Error **errp)
     }
 
     s->chr = chr;
-    chr->opaque = s;
+    drv = (VCDriverState *)chr;
+    drv->console = s;
 
     if (display_state) {
         text_console_do_init(chr, display_state);
@@ -2196,6 +2204,7 @@ static const TypeInfo qemu_console_info = {
 };
 
 static const CharDriver vc_driver = {
+    .instance_size = sizeof(VCDriverState),
     .kind = CHARDEV_BACKEND_KIND_VC,
     .parse = qemu_chr_parse_vc,
     .create = vc_init,
diff --git a/ui/gtk.c b/ui/gtk.c
index 608400b..90cf4d5 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -181,6 +181,12 @@ struct GtkDisplayState {
     bool ignore_keys;
 };
 
+typedef struct VCDriverState {
+    CharDriverState parent;
+    VirtualConsole *console;
+    bool echo;
+} VCDriverState;
+
 static void gd_grab_pointer(VirtualConsole *vc, const char *reason);
 static void gd_ungrab_pointer(GtkDisplayState *s);
 static void gd_grab_keyboard(VirtualConsole *vc, const char *reason);
@@ -1685,7 +1691,8 @@ static void gd_vc_adjustment_changed(GtkAdjustment *adjustment, void *opaque)
 
 static int gd_vc_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
 {
-    VirtualConsole *vc = chr->opaque;
+    VCDriverState *vcd = (VCDriverState *)chr;
+    VirtualConsole *vc = vcd->console;
 
     vte_terminal_feed(VTE_TERMINAL(vc->vte.terminal), (const char *)buf, len);
     return len;
@@ -1693,9 +1700,14 @@ static int gd_vc_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
 
 static void gd_vc_chr_set_echo(CharDriverState *chr, bool echo)
 {
-    VirtualConsole *vc = chr->opaque;
+    VCDriverState *vcd = (VCDriverState *)chr;
+    VirtualConsole *vc = vcd->console;
 
-    vc->vte.echo = echo;
+    if (vc) {
+        vc->vte.echo = echo;
+    } else {
+        vcd->echo = echo;
+    }
 }
 
 static int nb_vcs;
@@ -1704,6 +1716,7 @@ static CharDriverState *vcs[MAX_VCS];
 static CharDriverState *gd_vc_handler(ChardevVC *vc, Error **errp)
 {
     static const CharDriver gd_vc_driver = {
+        .instance_size = sizeof(VCDriverState),
         .kind = CHARDEV_BACKEND_KIND_VC,
         .chr_write = gd_vc_chr_write,
         .chr_set_echo = gd_vc_chr_set_echo,
@@ -1722,9 +1735,6 @@ static CharDriverState *gd_vc_handler(ChardevVC *vc, Error **errp)
         return NULL;
     }
 
-    /* Temporary, until gd_vc_vte_init runs.  */
-    chr->opaque = g_new0(VirtualConsole, 1);
-
     vcs[nb_vcs++] = chr;
 
     return chr;
@@ -1765,14 +1775,12 @@ static GSList *gd_vc_vte_init(GtkDisplayState *s, VirtualConsole *vc,
     GtkWidget *box;
     GtkWidget *scrollbar;
     GtkAdjustment *vadjustment;
-    VirtualConsole *tmp_vc = chr->opaque;
+    VCDriverState *vcd = (VCDriverState *)chr;
 
     vc->s = s;
-    vc->vte.echo = tmp_vc->vte.echo;
-
+    vc->vte.echo = vcd->echo;
     vc->vte.chr = chr;
-    chr->opaque = vc;
-    g_free(tmp_vc);
+    vcd->console = vc;
 
     snprintf(buffer, sizeof(buffer), "vc%d", idx);
     vc->label = g_strdup_printf("%s", vc->vte.chr->label
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [Qemu-devel] [PULL 32/41] bt: use qemu_chr_alloc()
  2017-01-27 13:45 [Qemu-devel] [PULL 00/41] Misc changes for 2017-01-27 Paolo Bonzini
                   ` (30 preceding siblings ...)
  2017-01-27 13:45 ` [Qemu-devel] [PULL 31/41] char: allocate CharDriverState as a single object Paolo Bonzini
@ 2017-01-27 13:45 ` Paolo Bonzini
  2017-01-27 13:45 ` [Qemu-devel] [PULL 33/41] char: rename CharDriverState Chardev Paolo Bonzini
                   ` (9 subsequent siblings)
  41 siblings, 0 replies; 43+ messages in thread
From: Paolo Bonzini @ 2017-01-27 13:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Use common allocator for CharDriverState.

Rename the now untouched parent field.

The casts added are temporary, they are replaced with QOM type-safe
macros in a later patch in this series.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 hw/bt/hci-csr.c | 19 +++++++++++++------
 1 file changed, 13 insertions(+), 6 deletions(-)

diff --git a/hw/bt/hci-csr.c b/hw/bt/hci-csr.c
index bf2deb0..e2c78b8 100644
--- a/hw/bt/hci-csr.c
+++ b/hw/bt/hci-csr.c
@@ -26,9 +26,10 @@
 #include "hw/irq.h"
 #include "sysemu/bt.h"
 #include "hw/bt.h"
+#include "qapi/error.h"
 
 struct csrhci_s {
-    CharDriverState chr;
+    CharDriverState parent;
     int enable;
     qemu_irq *pins;
     int pin_state;
@@ -78,7 +79,8 @@ enum {
 
 static inline void csrhci_fifo_wake(struct csrhci_s *s)
 {
-    CharBackend *be = s->chr.be;
+    CharDriverState *chr = (CharDriverState *)s;
+    CharBackend *be = chr->be;
 
     if (!s->enable || !s->out_len)
         return;
@@ -468,10 +470,15 @@ CharDriverState *uart_hci_init(void)
         .chr_write = csrhci_write,
         .chr_ioctl = csrhci_ioctl,
     };
-    struct csrhci_s *s = (struct csrhci_s *)
-            g_malloc0(sizeof(struct csrhci_s));
+    Error *err = NULL;
+    ChardevCommon common = { 0, };
+    CharDriverState *chr = qemu_chr_alloc(&hci_driver, &common, &err);
+    struct csrhci_s *s = (struct csrhci_s *)chr;
 
-    s->chr.driver = &hci_driver;
+    if (err) {
+        error_report_err(err);
+        return NULL;
+    }
 
     s->hci = qemu_next_hci();
     s->hci->opaque = s;
@@ -482,5 +489,5 @@ CharDriverState *uart_hci_init(void)
     s->pins = qemu_allocate_irqs(csrhci_pins, s, __csrhci_pins);
     csrhci_reset(s);
 
-    return &s->chr;
+    return chr;
 }
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [Qemu-devel] [PULL 33/41] char: rename CharDriverState Chardev
  2017-01-27 13:45 [Qemu-devel] [PULL 00/41] Misc changes for 2017-01-27 Paolo Bonzini
                   ` (31 preceding siblings ...)
  2017-01-27 13:45 ` [Qemu-devel] [PULL 32/41] bt: use qemu_chr_alloc() Paolo Bonzini
@ 2017-01-27 13:45 ` Paolo Bonzini
  2017-01-27 13:45 ` [Qemu-devel] [PULL 34/41] char: rename TCPChardev and NetChardev Paolo Bonzini
                   ` (8 subsequent siblings)
  41 siblings, 0 replies; 43+ messages in thread
From: Paolo Bonzini @ 2017-01-27 13:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Pick a uniform chardev type name.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 backends/baum.c                   |  56 +--
 backends/msmouse.c                |  42 +-
 backends/rng-egd.c                |   4 +-
 backends/testdev.c                |  28 +-
 gdbstub.c                         |  12 +-
 hw/arm/fsl-imx25.c                |   2 +-
 hw/arm/fsl-imx31.c                |   2 +-
 hw/arm/fsl-imx6.c                 |   2 +-
 hw/arm/nseries.c                  |   2 +-
 hw/arm/omap2.c                    |   2 +-
 hw/arm/pxa2xx.c                   |   2 +-
 hw/arm/virt.c                     |   2 +-
 hw/bt/hci-csr.c                   |  14 +-
 hw/char/escc.c                    |   2 +-
 hw/char/exynos4210_uart.c         |   2 +-
 hw/char/imx_serial.c              |   2 +-
 hw/char/mcf_uart.c                |   4 +-
 hw/char/omap_uart.c               |   6 +-
 hw/char/parallel.c                |   2 +-
 hw/char/serial-isa.c              |   2 +-
 hw/char/serial.c                  |   4 +-
 hw/char/sh_serial.c               |   2 +-
 hw/char/spapr_vty.c               |   2 +-
 hw/char/virtio-console.c          |   2 +-
 hw/core/qdev-properties-system.c  |   4 +-
 hw/display/milkymist-tmu2.c       |   2 +-
 hw/display/sm501.c                |   2 +-
 hw/isa/isa-bus.c                  |   2 +-
 hw/isa/pc87312.c                  |   2 +-
 hw/lm32/lm32.h                    |   4 +-
 hw/lm32/milkymist-hw.h            |   2 +-
 hw/mips/mips_malta.c              |   4 +-
 hw/misc/ivshmem.c                 |   2 +-
 hw/misc/milkymist-pfpu.c          |   2 +-
 hw/usb/ccid-card-passthru.c       |   2 +-
 hw/usb/dev-serial.c               |   6 +-
 hw/usb/redirect.c                 |   4 +-
 include/hw/arm/exynos4210.h       |   2 +-
 include/hw/arm/omap.h             |   6 +-
 include/hw/bt.h                   |   4 +-
 include/hw/char/cadence_uart.h    |   2 +-
 include/hw/char/escc.h            |   2 +-
 include/hw/char/pl011.h           |   4 +-
 include/hw/char/serial.h          |   4 +-
 include/hw/char/xilinx_uartlite.h |   2 +-
 include/hw/cris/etraxfs.h         |   2 +-
 include/hw/devices.h              |   2 +-
 include/hw/i386/pc.h              |   2 +-
 include/hw/m68k/mcf.h             |   4 +-
 include/hw/ppc/spapr_vio.h        |   2 +-
 include/hw/qdev-properties.h      |   2 +-
 include/hw/sh4/sh.h               |   2 +-
 include/hw/sparc/grlib.h          |   2 +-
 include/hw/xen/xen.h              |   2 +-
 include/monitor/monitor.h         |   2 +-
 include/qemu/typedefs.h           |   2 +-
 include/sysemu/char.h             |  94 ++---
 include/sysemu/replay.h           |   4 +-
 include/sysemu/sysemu.h           |   4 +-
 include/ui/gtk.h                  |   2 +-
 include/ui/qemu-spice.h           |   2 +-
 monitor.c                         |   4 +-
 net/colo-compare.c                |   4 +-
 net/filter-mirror.c               |   4 +-
 net/slirp.c                       |   2 +-
 net/vhost-user.c                  |  10 +-
 qemu-char.c                       | 839 +++++++++++++++++++-------------------
 qmp.c                             |   2 +-
 qtest.c                           |   2 +-
 replay/replay-char.c              |   8 +-
 spice-qemu-char.c                 |  80 ++--
 stubs/monitor.c                   |   2 +-
 stubs/replay.c                    |   4 +-
 tests/test-char.c                 |  10 +-
 tests/vhost-user-test.c           |   4 +-
 ui/console.c                      |  40 +-
 ui/gtk.c                          |  26 +-
 vl.c                              |  10 +-
 xen-common-stub.c                 |   2 +-
 xen-common.c                      |   4 +-
 80 files changed, 727 insertions(+), 724 deletions(-)

diff --git a/backends/baum.c b/backends/baum.c
index 4b48cf0..23d3c4a 100644
--- a/backends/baum.c
+++ b/backends/baum.c
@@ -85,7 +85,7 @@
 #define BUF_SIZE 256
 
 typedef struct {
-    CharDriverState parent;
+    Chardev parent;
 
     brlapi_handle_t *brlapi;
     int brlapi_fd;
@@ -98,7 +98,7 @@ typedef struct {
     uint8_t out_buf_used, out_buf_ptr;
 
     QEMUTimer *cellCount_timer;
-} BaumDriverState;
+} BaumChardev;
 
 /* Let's assume NABCC by default */
 enum way {
@@ -223,7 +223,7 @@ static const uint8_t nabcc_translation[2][256] = {
 };
 
 /* The guest OS has started discussing with us, finish initializing BrlAPI */
-static int baum_deferred_init(BaumDriverState *baum)
+static int baum_deferred_init(BaumChardev *baum)
 {
     int tty = BRLAPI_TTY_DEFAULT;
     QemuConsole *con;
@@ -253,9 +253,9 @@ static int baum_deferred_init(BaumDriverState *baum)
 }
 
 /* The serial port can receive more of our data */
-static void baum_accept_input(struct CharDriverState *chr)
+static void baum_accept_input(struct Chardev *chr)
 {
-    BaumDriverState *baum = (BaumDriverState *)chr;
+    BaumChardev *baum = (BaumChardev *)chr;
     int room, first;
 
     if (!baum->out_buf_used)
@@ -279,9 +279,9 @@ static void baum_accept_input(struct CharDriverState *chr)
 }
 
 /* We want to send a packet */
-static void baum_write_packet(BaumDriverState *baum, const uint8_t *buf, int len)
+static void baum_write_packet(BaumChardev *baum, const uint8_t *buf, int len)
 {
-    CharDriverState *chr = (CharDriverState *)baum;
+    Chardev *chr = (Chardev *)baum;
     uint8_t io_buf[1 + 2 * len], *cur = io_buf;
     int room;
     *cur++ = ESC;
@@ -322,14 +322,14 @@ static void baum_write_packet(BaumDriverState *baum, const uint8_t *buf, int len
 /* Called when the other end seems to have a wrong idea of our display size */
 static void baum_cellCount_timer_cb(void *opaque)
 {
-    BaumDriverState *baum = opaque;
+    BaumChardev *baum = opaque;
     uint8_t cell_count[] = { BAUM_RSP_CellCount, baum->x * baum->y };
     DPRINTF("Timeout waiting for DisplayData, sending cell count\n");
     baum_write_packet(baum, cell_count, sizeof(cell_count));
 }
 
 /* Try to interpret a whole incoming packet */
-static int baum_eat_packet(BaumDriverState *baum, const uint8_t *buf, int len)
+static int baum_eat_packet(BaumChardev *baum, const uint8_t *buf, int len)
 {
     const uint8_t *cur = buf;
     uint8_t req = 0;
@@ -470,9 +470,9 @@ static int baum_eat_packet(BaumDriverState *baum, const uint8_t *buf, int len)
 }
 
 /* The other end is writing some data.  Store it and try to interpret */
-static int baum_write(CharDriverState *chr, const uint8_t *buf, int len)
+static int baum_write(Chardev *chr, const uint8_t *buf, int len)
 {
-    BaumDriverState *baum = (BaumDriverState *)chr;
+    BaumChardev *baum = (BaumChardev *)chr;
     int tocopy, cur, eaten, orig_len = len;
 
     if (!len)
@@ -511,14 +511,16 @@ static int baum_write(CharDriverState *chr, const uint8_t *buf, int len)
 }
 
 /* Send the key code to the other end */
-static void baum_send_key(BaumDriverState *baum, uint8_t type, uint8_t value) {
+static void baum_send_key(BaumChardev *baum, uint8_t type, uint8_t value)
+{
     uint8_t packet[] = { type, value };
     DPRINTF("writing key %x %x\n", type, value);
     baum_write_packet(baum, packet, sizeof(packet));
 }
 
-static void baum_send_key2(BaumDriverState *baum, uint8_t type, uint8_t value,
-                           uint8_t value2) {
+static void baum_send_key2(BaumChardev *baum, uint8_t type, uint8_t value,
+                           uint8_t value2)
+{
     uint8_t packet[] = { type, value, value2 };
     DPRINTF("writing key %x %x\n", type, value);
     baum_write_packet(baum, packet, sizeof(packet));
@@ -527,7 +529,7 @@ static void baum_send_key2(BaumDriverState *baum, uint8_t type, uint8_t value,
 /* We got some data on the BrlAPI socket */
 static void baum_chr_read(void *opaque)
 {
-    BaumDriverState *baum = opaque;
+    BaumChardev *baum = opaque;
     brlapi_keyCode_t code;
     int ret;
     if (!baum->brlapi)
@@ -611,9 +613,9 @@ static void baum_chr_read(void *opaque)
     }
 }
 
-static void baum_free(struct CharDriverState *chr)
+static void baum_free(struct Chardev *chr)
 {
-    BaumDriverState *baum = (BaumDriverState *)chr;
+    BaumChardev *baum = (BaumChardev *)chr;
 
     timer_free(baum->cellCount_timer);
     if (baum->brlapi) {
@@ -622,23 +624,23 @@ static void baum_free(struct CharDriverState *chr)
     }
 }
 
-static CharDriverState *chr_baum_init(const CharDriver *driver,
-                                      const char *id,
-                                      ChardevBackend *backend,
-                                      ChardevReturn *ret,
-                                      bool *be_opened,
-                                      Error **errp)
+static Chardev *chr_baum_init(const CharDriver *driver,
+                              const char *id,
+                              ChardevBackend *backend,
+                              ChardevReturn *ret,
+                              bool *be_opened,
+                              Error **errp)
 {
     ChardevCommon *common = backend->u.braille.data;
-    BaumDriverState *baum;
-    CharDriverState *chr;
+    BaumChardev *baum;
+    Chardev *chr;
     brlapi_handle_t *handle;
 
     chr = qemu_chr_alloc(driver, common, errp);
     if (!chr) {
         return NULL;
     }
-    baum = (BaumDriverState *)chr;
+    baum = (BaumChardev *)chr;
 
     handle = g_malloc0(brlapi_getHandleSize());
     baum->brlapi = handle;
@@ -666,7 +668,7 @@ fail_handle:
 static void register_types(void)
 {
     static const CharDriver driver = {
-        .instance_size = sizeof(BaumDriverState),
+        .instance_size = sizeof(BaumChardev),
         .kind = CHARDEV_BACKEND_KIND_BRAILLE,
         .create = chr_baum_init,
         .chr_write = baum_write,
diff --git a/backends/msmouse.c b/backends/msmouse.c
index 30087b3..4e474da 100644
--- a/backends/msmouse.c
+++ b/backends/msmouse.c
@@ -31,7 +31,7 @@
 #define MSMOUSE_HI2(n) (((n) & 0xc0) >> 6)
 
 typedef struct {
-    CharDriverState parent;
+    Chardev parent;
 
     QemuInputHandlerState *hs;
     int axis[INPUT_AXIS__MAX];
@@ -39,11 +39,11 @@ typedef struct {
     bool btnc[INPUT_BUTTON__MAX];
     uint8_t outbuf[32];
     int outlen;
-} MouseState;
+} MouseChardev;
 
-static void msmouse_chr_accept_input(CharDriverState *chr)
+static void msmouse_chr_accept_input(Chardev *chr)
 {
-    MouseState *mouse = (MouseState *)chr;
+    MouseChardev *mouse = (MouseChardev *)chr;
     int len;
 
     len = qemu_chr_be_can_write(chr);
@@ -61,7 +61,7 @@ static void msmouse_chr_accept_input(CharDriverState *chr)
     }
 }
 
-static void msmouse_queue_event(MouseState *mouse)
+static void msmouse_queue_event(MouseChardev *mouse)
 {
     unsigned char bytes[4] = { 0x40, 0x00, 0x00, 0x00 };
     int dx, dy, count = 3;
@@ -98,7 +98,7 @@ static void msmouse_queue_event(MouseState *mouse)
 static void msmouse_input_event(DeviceState *dev, QemuConsole *src,
                                 InputEvent *evt)
 {
-    MouseState *mouse = (MouseState *)dev;
+    MouseChardev *mouse = (MouseChardev *)dev;
     InputMoveEvent *move;
     InputBtnEvent *btn;
 
@@ -122,22 +122,22 @@ static void msmouse_input_event(DeviceState *dev, QemuConsole *src,
 
 static void msmouse_input_sync(DeviceState *dev)
 {
-    MouseState *mouse = (MouseState *)dev;
-    CharDriverState *chr = (CharDriverState *)dev;
+    MouseChardev *mouse = (MouseChardev *)dev;
+    Chardev *chr = (Chardev *)dev;
 
     msmouse_queue_event(mouse);
     msmouse_chr_accept_input(chr);
 }
 
-static int msmouse_chr_write (struct CharDriverState *s, const uint8_t *buf, int len)
+static int msmouse_chr_write(struct Chardev *s, const uint8_t *buf, int len)
 {
     /* Ignore writes to mouse port */
     return len;
 }
 
-static void msmouse_chr_free(struct CharDriverState *chr)
+static void msmouse_chr_free(struct Chardev *chr)
 {
-    MouseState *mouse = (MouseState *)chr;
+    MouseChardev *mouse = (MouseChardev *)chr;
 
     qemu_input_handler_unregister(mouse->hs);
 }
@@ -149,16 +149,16 @@ static QemuInputHandler msmouse_handler = {
     .sync  = msmouse_input_sync,
 };
 
-static CharDriverState *qemu_chr_open_msmouse(const CharDriver *driver,
-                                              const char *id,
-                                              ChardevBackend *backend,
-                                              ChardevReturn *ret,
-                                              bool *be_opened,
-                                              Error **errp)
+static Chardev *qemu_chr_open_msmouse(const CharDriver *driver,
+                                      const char *id,
+                                      ChardevBackend *backend,
+                                      ChardevReturn *ret,
+                                      bool *be_opened,
+                                      Error **errp)
 {
     ChardevCommon *common = backend->u.msmouse.data;
-    MouseState *mouse;
-    CharDriverState *chr;
+    MouseChardev *mouse;
+    Chardev *chr;
 
     chr = qemu_chr_alloc(driver, common, errp);
     if (!chr) {
@@ -166,7 +166,7 @@ static CharDriverState *qemu_chr_open_msmouse(const CharDriver *driver,
     }
     *be_opened = false;
 
-    mouse = (MouseState *)chr;
+    mouse = (MouseChardev *)chr;
     mouse->hs = qemu_input_handler_register((DeviceState *)mouse,
                                             &msmouse_handler);
 
@@ -177,7 +177,7 @@ static CharDriverState *qemu_chr_open_msmouse(const CharDriver *driver,
 static void register_types(void)
 {
     static const CharDriver driver = {
-        .instance_size = sizeof(MouseState),
+        .instance_size = sizeof(MouseChardev),
         .kind = CHARDEV_BACKEND_KIND_MSMOUSE,
         .create = qemu_chr_open_msmouse,
         .chr_write = msmouse_chr_write,
diff --git a/backends/rng-egd.c b/backends/rng-egd.c
index 69c04b1..380b19a 100644
--- a/backends/rng-egd.c
+++ b/backends/rng-egd.c
@@ -86,7 +86,7 @@ static void rng_egd_chr_read(void *opaque, const uint8_t *buf, int size)
 static void rng_egd_opened(RngBackend *b, Error **errp)
 {
     RngEgd *s = RNG_EGD(b);
-    CharDriverState *chr;
+    Chardev *chr;
 
     if (s->chr_name == NULL) {
         error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
@@ -125,7 +125,7 @@ static void rng_egd_set_chardev(Object *obj, const char *value, Error **errp)
 static char *rng_egd_get_chardev(Object *obj, Error **errp)
 {
     RngEgd *s = RNG_EGD(obj);
-    CharDriverState *chr = qemu_chr_fe_get_driver(&s->chr);
+    Chardev *chr = qemu_chr_fe_get_driver(&s->chr);
 
     if (chr && chr->label) {
         return g_strdup(chr->label);
diff --git a/backends/testdev.c b/backends/testdev.c
index dfa4fe1..268b380 100644
--- a/backends/testdev.c
+++ b/backends/testdev.c
@@ -30,14 +30,14 @@
 #define BUF_SIZE 32
 
 typedef struct {
-    CharDriverState parent;
+    Chardev parent;
 
     uint8_t in_buf[32];
     int in_buf_used;
-} TestdevCharState;
+} TestdevChardev;
 
 /* Try to interpret a whole incoming packet */
-static int testdev_eat_packet(TestdevCharState *testdev)
+static int testdev_eat_packet(TestdevChardev *testdev)
 {
     const uint8_t *cur = testdev->in_buf;
     int len = testdev->in_buf_used;
@@ -78,9 +78,9 @@ static int testdev_eat_packet(TestdevCharState *testdev)
 }
 
 /* The other end is writing some data.  Store it and try to interpret */
-static int testdev_write(CharDriverState *chr, const uint8_t *buf, int len)
+static int testdev_write(Chardev *chr, const uint8_t *buf, int len)
 {
-    TestdevCharState *testdev = (TestdevCharState *)chr;
+    TestdevChardev *testdev = (TestdevChardev *)chr;
     int tocopy, eaten, orig_len = len;
 
     while (len) {
@@ -103,15 +103,15 @@ static int testdev_write(CharDriverState *chr, const uint8_t *buf, int len)
     return orig_len;
 }
 
-static CharDriverState *chr_testdev_init(const CharDriver *driver,
-                                         const char *id,
-                                         ChardevBackend *backend,
-                                         ChardevReturn *ret,
-                                         bool *be_opened,
-                                         Error **errp)
+static Chardev *chr_testdev_init(const CharDriver *driver,
+                                 const char *id,
+                                 ChardevBackend *backend,
+                                 ChardevReturn *ret,
+                                 bool *be_opened,
+                                 Error **errp)
 {
-    TestdevCharState *testdev = g_new0(TestdevCharState, 1);;
-    CharDriverState *chr = (CharDriverState *)testdev;
+    TestdevChardev *testdev = g_new0(TestdevChardev, 1);;
+    Chardev *chr = (Chardev *)testdev;
 
     chr->driver = driver;
 
@@ -121,7 +121,7 @@ static CharDriverState *chr_testdev_init(const CharDriver *driver,
 static void register_types(void)
 {
     static const CharDriver driver = {
-        .instance_size = sizeof(TestdevCharState),
+        .instance_size = sizeof(TestdevChardev),
         .kind = CHARDEV_BACKEND_KIND_TESTDEV,
         .create = chr_testdev_init,
         .chr_write = testdev_write,
diff --git a/gdbstub.c b/gdbstub.c
index 719e413..396f892 100644
--- a/gdbstub.c
+++ b/gdbstub.c
@@ -304,7 +304,7 @@ typedef struct GDBState {
     int running_state;
 #else
     CharBackend chr;
-    CharDriverState *mon_chr;
+    Chardev *mon_chr;
 #endif
     char syscall_buf[256];
     gdb_syscall_complete_cb current_syscall_cb;
@@ -1472,7 +1472,7 @@ void gdb_exit(CPUArchState *env, int code)
   GDBState *s;
   char buf[4];
 #ifndef CONFIG_USER_ONLY
-  CharDriverState *chr;
+  Chardev *chr;
 #endif
 
   s = gdbserver_state;
@@ -1697,7 +1697,7 @@ static void gdb_monitor_output(GDBState *s, const char *msg, int len)
     put_packet(s, buf);
 }
 
-static int gdb_monitor_write(CharDriverState *chr, const uint8_t *buf, int len)
+static int gdb_monitor_write(Chardev *chr, const uint8_t *buf, int len)
 {
     const char *p = (const char *)buf;
     int max_sz;
@@ -1728,11 +1728,11 @@ int gdbserver_start(const char *device)
 {
     GDBState *s;
     char gdbstub_device_name[128];
-    CharDriverState *chr = NULL;
-    CharDriverState *mon_chr;
+    Chardev *chr = NULL;
+    Chardev *mon_chr;
     ChardevCommon common = { 0 };
     static const CharDriver driver = {
-        .instance_size = sizeof(CharDriverState),
+        .instance_size = sizeof(Chardev),
         .kind = -1,
         .chr_write = gdb_monitor_write,
     };
diff --git a/hw/arm/fsl-imx25.c b/hw/arm/fsl-imx25.c
index 7bb7be7..2126f73 100644
--- a/hw/arm/fsl-imx25.c
+++ b/hw/arm/fsl-imx25.c
@@ -118,7 +118,7 @@ static void fsl_imx25_realize(DeviceState *dev, Error **errp)
         };
 
         if (i < MAX_SERIAL_PORTS) {
-            CharDriverState *chr;
+            Chardev *chr;
 
             chr = serial_hds[i];
 
diff --git a/hw/arm/fsl-imx31.c b/hw/arm/fsl-imx31.c
index f23672b..dd1c713 100644
--- a/hw/arm/fsl-imx31.c
+++ b/hw/arm/fsl-imx31.c
@@ -107,7 +107,7 @@ static void fsl_imx31_realize(DeviceState *dev, Error **errp)
         };
 
         if (i < MAX_SERIAL_PORTS) {
-            CharDriverState *chr;
+            Chardev *chr;
 
             chr = serial_hds[i];
 
diff --git a/hw/arm/fsl-imx6.c b/hw/arm/fsl-imx6.c
index e93532f..76dd8a4 100644
--- a/hw/arm/fsl-imx6.c
+++ b/hw/arm/fsl-imx6.c
@@ -187,7 +187,7 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp)
         };
 
         if (i < MAX_SERIAL_PORTS) {
-            CharDriverState *chr;
+            Chardev *chr;
 
             chr = serial_hds[i];
 
diff --git a/hw/arm/nseries.c b/hw/arm/nseries.c
index c86cf80..503a3b6 100644
--- a/hw/arm/nseries.c
+++ b/hw/arm/nseries.c
@@ -786,7 +786,7 @@ static void n8x0_cbus_setup(struct n800_s *s)
 
 static void n8x0_uart_setup(struct n800_s *s)
 {
-    CharDriverState *radio = uart_hci_init();
+    Chardev *radio = uart_hci_init();
 
     qdev_connect_gpio_out(s->mpu->gpio, N8X0_BT_RESET_GPIO,
                     csrhci_pins_get(radio)[csrhci_pin_reset]);
diff --git a/hw/arm/omap2.c b/hw/arm/omap2.c
index 6f05c98..cf1b4ba 100644
--- a/hw/arm/omap2.c
+++ b/hw/arm/omap2.c
@@ -792,7 +792,7 @@ static const MemoryRegionOps omap_sti_fifo_ops = {
 static struct omap_sti_s *omap_sti_init(struct omap_target_agent_s *ta,
                 MemoryRegion *sysmem,
                 hwaddr channel_base, qemu_irq irq, omap_clk clk,
-                CharDriverState *chr)
+                Chardev *chr)
 {
     struct omap_sti_s *s = g_new0(struct omap_sti_s, 1);
 
diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c
index d31b457..cfee392 100644
--- a/hw/arm/pxa2xx.c
+++ b/hw/arm/pxa2xx.c
@@ -2024,7 +2024,7 @@ static PXA2xxFIrState *pxa2xx_fir_init(MemoryRegion *sysmem,
                                        hwaddr base,
                                        qemu_irq irq, qemu_irq rx_dma,
                                        qemu_irq tx_dma,
-                                       CharDriverState *chr)
+                                       Chardev *chr)
 {
     DeviceState *dev;
     SysBusDevice *sbd;
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 6c9e898..1f216cf 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -613,7 +613,7 @@ static void create_gic(VirtMachineState *vms, qemu_irq *pic)
 }
 
 static void create_uart(const VirtMachineState *vms, qemu_irq *pic, int uart,
-                        MemoryRegion *mem, CharDriverState *chr)
+                        MemoryRegion *mem, Chardev *chr)
 {
     char *nodename;
     hwaddr base = vms->memmap[uart].base;
diff --git a/hw/bt/hci-csr.c b/hw/bt/hci-csr.c
index e2c78b8..153ed2f 100644
--- a/hw/bt/hci-csr.c
+++ b/hw/bt/hci-csr.c
@@ -29,7 +29,7 @@
 #include "qapi/error.h"
 
 struct csrhci_s {
-    CharDriverState parent;
+    Chardev parent;
     int enable;
     qemu_irq *pins;
     int pin_state;
@@ -79,7 +79,7 @@ enum {
 
 static inline void csrhci_fifo_wake(struct csrhci_s *s)
 {
-    CharDriverState *chr = (CharDriverState *)s;
+    Chardev *chr = (Chardev *)s;
     CharBackend *be = chr->be;
 
     if (!s->enable || !s->out_len)
@@ -313,7 +313,7 @@ static void csrhci_ready_for_next_inpkt(struct csrhci_s *s)
     s->in_hdr = INT_MAX;
 }
 
-static int csrhci_write(struct CharDriverState *chr,
+static int csrhci_write(struct Chardev *chr,
                 const uint8_t *buf, int len)
 {
     struct csrhci_s *s = (struct csrhci_s *)chr;
@@ -386,7 +386,7 @@ static void csrhci_out_hci_packet_acl(void *opaque,
     csrhci_fifo_wake(s);
 }
 
-static int csrhci_ioctl(struct CharDriverState *chr, int cmd, void *arg)
+static int csrhci_ioctl(struct Chardev *chr, int cmd, void *arg)
 {
     QEMUSerialSetParams *ssp;
     struct csrhci_s *s = (struct csrhci_s *) chr;
@@ -455,14 +455,14 @@ static void csrhci_pins(void *opaque, int line, int level)
     }
 }
 
-qemu_irq *csrhci_pins_get(CharDriverState *chr)
+qemu_irq *csrhci_pins_get(Chardev *chr)
 {
     struct csrhci_s *s = (struct csrhci_s *) chr;
 
     return s->pins;
 }
 
-CharDriverState *uart_hci_init(void)
+Chardev *uart_hci_init(void)
 {
     static const CharDriver hci_driver = {
         .instance_size = sizeof(struct csrhci_s),
@@ -472,7 +472,7 @@ CharDriverState *uart_hci_init(void)
     };
     Error *err = NULL;
     ChardevCommon common = { 0, };
-    CharDriverState *chr = qemu_chr_alloc(&hci_driver, &common, &err);
+    Chardev *chr = qemu_chr_alloc(&hci_driver, &common, &err);
     struct csrhci_s *s = (struct csrhci_s *)chr;
 
     if (err) {
diff --git a/hw/char/escc.c b/hw/char/escc.c
index d6662dc..9228091 100644
--- a/hw/char/escc.c
+++ b/hw/char/escc.c
@@ -689,7 +689,7 @@ static const VMStateDescription vmstate_escc = {
 };
 
 MemoryRegion *escc_init(hwaddr base, qemu_irq irqA, qemu_irq irqB,
-              CharDriverState *chrA, CharDriverState *chrB,
+              Chardev *chrA, Chardev *chrB,
               int clock, int it_shift)
 {
     DeviceState *dev;
diff --git a/hw/char/exynos4210_uart.c b/hw/char/exynos4210_uart.c
index 820d1ab..565b27e 100644
--- a/hw/char/exynos4210_uart.c
+++ b/hw/char/exynos4210_uart.c
@@ -586,7 +586,7 @@ static const VMStateDescription vmstate_exynos4210_uart = {
 DeviceState *exynos4210_uart_create(hwaddr addr,
                                     int fifo_size,
                                     int channel,
-                                    CharDriverState *chr,
+                                    Chardev *chr,
                                     qemu_irq irq)
 {
     DeviceState  *dev;
diff --git a/hw/char/imx_serial.c b/hw/char/imx_serial.c
index 99545fc..52e67f8 100644
--- a/hw/char/imx_serial.c
+++ b/hw/char/imx_serial.c
@@ -170,7 +170,7 @@ static void imx_serial_write(void *opaque, hwaddr offset,
                              uint64_t value, unsigned size)
 {
     IMXSerialState *s = (IMXSerialState *)opaque;
-    CharDriverState *chr = qemu_chr_fe_get_driver(&s->chr);
+    Chardev *chr = qemu_chr_fe_get_driver(&s->chr);
     unsigned char ch;
 
     DPRINTF("write(offset=0x%" HWADDR_PRIx ", value = 0x%x) to %s\n",
diff --git a/hw/char/mcf_uart.c b/hw/char/mcf_uart.c
index ecaa091..80c380e 100644
--- a/hw/char/mcf_uart.c
+++ b/hw/char/mcf_uart.c
@@ -275,7 +275,7 @@ static void mcf_uart_receive(void *opaque, const uint8_t *buf, int size)
     mcf_uart_push_byte(s, buf[0]);
 }
 
-void *mcf_uart_init(qemu_irq irq, CharDriverState *chr)
+void *mcf_uart_init(qemu_irq irq, Chardev *chr)
 {
     mcf_uart_state *s;
 
@@ -300,7 +300,7 @@ static const MemoryRegionOps mcf_uart_ops = {
 void mcf_uart_mm_init(MemoryRegion *sysmem,
                       hwaddr base,
                       qemu_irq irq,
-                      CharDriverState *chr)
+                      Chardev *chr)
 {
     mcf_uart_state *s;
 
diff --git a/hw/char/omap_uart.c b/hw/char/omap_uart.c
index 893ab10..31ebb15 100644
--- a/hw/char/omap_uart.c
+++ b/hw/char/omap_uart.c
@@ -54,7 +54,7 @@ void omap_uart_reset(struct omap_uart_s *s)
 struct omap_uart_s *omap_uart_init(hwaddr base,
                 qemu_irq irq, omap_clk fclk, omap_clk iclk,
                 qemu_irq txdma, qemu_irq rxdma,
-                const char *label, CharDriverState *chr)
+                const char *label, Chardev *chr)
 {
     struct omap_uart_s *s = g_new0(struct omap_uart_s, 1);
 
@@ -163,7 +163,7 @@ struct omap_uart_s *omap2_uart_init(MemoryRegion *sysmem,
                 struct omap_target_agent_s *ta,
                 qemu_irq irq, omap_clk fclk, omap_clk iclk,
                 qemu_irq txdma, qemu_irq rxdma,
-                const char *label, CharDriverState *chr)
+                const char *label, Chardev *chr)
 {
     hwaddr base = omap_l4_attach(ta, 0, NULL);
     struct omap_uart_s *s = omap_uart_init(base, irq,
@@ -178,7 +178,7 @@ struct omap_uart_s *omap2_uart_init(MemoryRegion *sysmem,
     return s;
 }
 
-void omap_uart_attach(struct omap_uart_s *s, CharDriverState *chr)
+void omap_uart_attach(struct omap_uart_s *s, Chardev *chr)
 {
     /* TODO: Should reuse or destroy current s->serial */
     s->serial = serial_mm_init(get_system_memory(), s->base, 2, s->irq,
diff --git a/hw/char/parallel.c b/hw/char/parallel.c
index f2d5666..c71a4a0 100644
--- a/hw/char/parallel.c
+++ b/hw/char/parallel.c
@@ -603,7 +603,7 @@ static const MemoryRegionOps parallel_mm_ops = {
 /* 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,
-                      CharDriverState *chr)
+                      Chardev *chr)
 {
     ParallelState *s;
 
diff --git a/hw/char/serial-isa.c b/hw/char/serial-isa.c
index 54d3a12..d7c5cc1 100644
--- a/hw/char/serial-isa.c
+++ b/hw/char/serial-isa.c
@@ -121,7 +121,7 @@ static void serial_register_types(void)
 
 type_init(serial_register_types)
 
-static void serial_isa_init(ISABus *bus, int index, CharDriverState *chr)
+static void serial_isa_init(ISABus *bus, int index, Chardev *chr)
 {
     DeviceState *dev;
     ISADevice *isadev;
diff --git a/hw/char/serial.c b/hw/char/serial.c
index 67b18ed..03d890c 100644
--- a/hw/char/serial.c
+++ b/hw/char/serial.c
@@ -937,7 +937,7 @@ const MemoryRegionOps serial_io_ops = {
 };
 
 SerialState *serial_init(int base, qemu_irq irq, int baudbase,
-                         CharDriverState *chr, MemoryRegion *system_io)
+                         Chardev *chr, MemoryRegion *system_io)
 {
     SerialState *s;
 
@@ -993,7 +993,7 @@ static const MemoryRegionOps serial_mm_ops[3] = {
 SerialState *serial_mm_init(MemoryRegion *address_space,
                             hwaddr base, int it_shift,
                             qemu_irq irq, int baudbase,
-                            CharDriverState *chr, enum device_endian end)
+                            Chardev *chr, enum device_endian end)
 {
     SerialState *s;
 
diff --git a/hw/char/sh_serial.c b/hw/char/sh_serial.c
index 9d35564..303eb0a 100644
--- a/hw/char/sh_serial.c
+++ b/hw/char/sh_serial.c
@@ -356,7 +356,7 @@ static const MemoryRegionOps sh_serial_ops = {
 
 void sh_serial_init(MemoryRegion *sysmem,
                     hwaddr base, int feat,
-                    uint32_t freq, CharDriverState *chr,
+                    uint32_t freq, Chardev *chr,
                     qemu_irq eri_source,
                     qemu_irq rxi_source,
                     qemu_irq txi_source,
diff --git a/hw/char/spapr_vty.c b/hw/char/spapr_vty.c
index 7c22b8b..e30c8da 100644
--- a/hw/char/spapr_vty.c
+++ b/hw/char/spapr_vty.c
@@ -141,7 +141,7 @@ static target_ulong h_get_term_char(PowerPCCPU *cpu, sPAPRMachineState *spapr,
     return H_SUCCESS;
 }
 
-void spapr_vty_create(VIOsPAPRBus *bus, CharDriverState *chardev)
+void spapr_vty_create(VIOsPAPRBus *bus, Chardev *chardev)
 {
     DeviceState *dev;
 
diff --git a/hw/char/virtio-console.c b/hw/char/virtio-console.c
index 776205b..798d9b6 100644
--- a/hw/char/virtio-console.c
+++ b/hw/char/virtio-console.c
@@ -168,7 +168,7 @@ static void virtconsole_realize(DeviceState *dev, Error **errp)
     VirtIOSerialPort *port = VIRTIO_SERIAL_PORT(dev);
     VirtConsole *vcon = VIRTIO_CONSOLE(dev);
     VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_GET_CLASS(dev);
-    CharDriverState *chr = qemu_chr_fe_get_driver(&vcon->chr);
+    Chardev *chr = qemu_chr_fe_get_driver(&vcon->chr);
 
     if (port->id == 0 && !k->is_console) {
         error_setg(errp, "Port number 0 on virtio-serial devices reserved "
diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c
index 1b7ea50..94f4d8b 100644
--- a/hw/core/qdev-properties-system.c
+++ b/hw/core/qdev-properties-system.c
@@ -179,7 +179,7 @@ static void set_chr(Object *obj, Visitor *v, const char *name, void *opaque,
     Error *local_err = NULL;
     Property *prop = opaque;
     CharBackend *be = qdev_get_prop_ptr(dev, prop);
-    CharDriverState *s;
+    Chardev *s;
     char *str;
 
     if (dev->realized) {
@@ -411,7 +411,7 @@ void qdev_prop_set_drive(DeviceState *dev, const char *name,
 }
 
 void qdev_prop_set_chr(DeviceState *dev, const char *name,
-                       CharDriverState *value)
+                       Chardev *value)
 {
     assert(!value || value->label);
     object_property_set_str(OBJECT(dev),
diff --git a/hw/display/milkymist-tmu2.c b/hw/display/milkymist-tmu2.c
index 5c666f9..18f27fc 100644
--- a/hw/display/milkymist-tmu2.c
+++ b/hw/display/milkymist-tmu2.c
@@ -85,7 +85,7 @@ struct MilkymistTMU2State {
     SysBusDevice parent_obj;
 
     MemoryRegion regs_region;
-    CharDriverState *chr;
+    Chardev *chr;
     qemu_irq irq;
 
     uint32_t regs[R_MAX];
diff --git a/hw/display/sm501.c b/hw/display/sm501.c
index 5f71012..040a0b9 100644
--- a/hw/display/sm501.c
+++ b/hw/display/sm501.c
@@ -1392,7 +1392,7 @@ static const GraphicHwOps sm501_ops = {
 };
 
 void sm501_init(MemoryRegion *address_space_mem, uint32_t base,
-                uint32_t local_mem_bytes, qemu_irq irq, CharDriverState *chr)
+                uint32_t local_mem_bytes, qemu_irq irq, Chardev *chr)
 {
     SM501State * s;
     DeviceState *dev;
diff --git a/hw/isa/isa-bus.c b/hw/isa/isa-bus.c
index 9d07b11..166ac93 100644
--- a/hw/isa/isa-bus.c
+++ b/hw/isa/isa-bus.c
@@ -287,7 +287,7 @@ MemoryRegion *isa_address_space_io(ISADevice *dev)
 
 type_init(isabus_register_types)
 
-static void parallel_init(ISABus *bus, int index, CharDriverState *chr)
+static void parallel_init(ISABus *bus, int index, Chardev *chr)
 {
     DeviceState *dev;
     ISADevice *isadev;
diff --git a/hw/isa/pc87312.c b/hw/isa/pc87312.c
index b1c1a0a..c707d24 100644
--- a/hw/isa/pc87312.c
+++ b/hw/isa/pc87312.c
@@ -268,7 +268,7 @@ static void pc87312_realize(DeviceState *dev, Error **errp)
     DeviceState *d;
     ISADevice *isa;
     ISABus *bus;
-    CharDriverState *chr;
+    Chardev *chr;
     DriveInfo *drive;
     char name[5];
     int i;
diff --git a/hw/lm32/lm32.h b/hw/lm32/lm32.h
index db9eb29..d1514a6 100644
--- a/hw/lm32/lm32.h
+++ b/hw/lm32/lm32.h
@@ -16,7 +16,7 @@ static inline DeviceState *lm32_pic_init(qemu_irq cpu_irq)
     return dev;
 }
 
-static inline DeviceState *lm32_juart_init(CharDriverState *chr)
+static inline DeviceState *lm32_juart_init(Chardev *chr)
 {
     DeviceState *dev;
 
@@ -29,7 +29,7 @@ static inline DeviceState *lm32_juart_init(CharDriverState *chr)
 
 static inline DeviceState *lm32_uart_create(hwaddr addr,
                                             qemu_irq irq,
-                                            CharDriverState *chr)
+                                            Chardev *chr)
 {
     DeviceState *dev;
     SysBusDevice *s;
diff --git a/hw/lm32/milkymist-hw.h b/hw/lm32/milkymist-hw.h
index 4418b44..d3be0cf 100644
--- a/hw/lm32/milkymist-hw.h
+++ b/hw/lm32/milkymist-hw.h
@@ -6,7 +6,7 @@
 
 static inline DeviceState *milkymist_uart_create(hwaddr base,
                                                  qemu_irq irq,
-                                                 CharDriverState *chr)
+                                                 Chardev *chr)
 {
     DeviceState *dev;
 
diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c
index cf48f42..75877de 100644
--- a/hw/mips/mips_malta.c
+++ b/hw/mips/mips_malta.c
@@ -551,10 +551,10 @@ static void malta_fgpa_display_event(void *opaque, int event)
 }
 
 static MaltaFPGAState *malta_fpga_init(MemoryRegion *address_space,
-         hwaddr base, qemu_irq uart_irq, CharDriverState *uart_chr)
+         hwaddr base, qemu_irq uart_irq, Chardev *uart_chr)
 {
     MaltaFPGAState *s;
-    CharDriverState *chr;
+    Chardev *chr;
 
     s = (MaltaFPGAState *)g_malloc0(sizeof(MaltaFPGAState));
 
diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem.c
index abeaf3d..c836222 100644
--- a/hw/misc/ivshmem.c
+++ b/hw/misc/ivshmem.c
@@ -868,7 +868,7 @@ static void ivshmem_common_realize(PCIDevice *dev, Error **errp)
         s->ivshmem_bar2 = host_memory_backend_get_memory(s->hostmem,
                                                          &error_abort);
     } else {
-        CharDriverState *chr = qemu_chr_fe_get_driver(&s->server_chr);
+        Chardev *chr = qemu_chr_fe_get_driver(&s->server_chr);
         assert(chr);
 
         IVSHMEM_DPRINTF("using shared memory server (socket = %s)\n",
diff --git a/hw/misc/milkymist-pfpu.c b/hw/misc/milkymist-pfpu.c
index 3ca2589..86f5e38 100644
--- a/hw/misc/milkymist-pfpu.c
+++ b/hw/misc/milkymist-pfpu.c
@@ -125,7 +125,7 @@ struct MilkymistPFPUState {
     SysBusDevice parent_obj;
 
     MemoryRegion regs_region;
-    CharDriverState *chr;
+    Chardev *chr;
     qemu_irq irq;
 
     uint32_t regs[R_MAX];
diff --git a/hw/usb/ccid-card-passthru.c b/hw/usb/ccid-card-passthru.c
index 88cb6d8..daab0d5 100644
--- a/hw/usb/ccid-card-passthru.c
+++ b/hw/usb/ccid-card-passthru.c
@@ -264,7 +264,7 @@ static void ccid_card_vscard_handle_message(PassthruState *card,
 
 static void ccid_card_vscard_drop_connection(PassthruState *card)
 {
-    CharDriverState *chr = qemu_chr_fe_get_driver(&card->cs);
+    Chardev *chr = qemu_chr_fe_get_driver(&card->cs);
 
     qemu_chr_fe_deinit(&card->cs);
     qemu_chr_delete(chr);
diff --git a/hw/usb/dev-serial.c b/hw/usb/dev-serial.c
index 6066d9b..6d51373 100644
--- a/hw/usb/dev-serial.c
+++ b/hw/usb/dev-serial.c
@@ -483,7 +483,7 @@ static void usb_serial_realize(USBDevice *dev, Error **errp)
 {
     USBSerialState *s = USB_SERIAL_DEV(dev);
     Error *local_err = NULL;
-    CharDriverState *chr = qemu_chr_fe_get_driver(&s->cs);
+    Chardev *chr = qemu_chr_fe_get_driver(&s->cs);
 
     usb_desc_create_serial(dev);
     usb_desc_init(dev);
@@ -512,7 +512,7 @@ static void usb_serial_realize(USBDevice *dev, Error **errp)
 static USBDevice *usb_serial_init(USBBus *bus, const char *filename)
 {
     USBDevice *dev;
-    CharDriverState *cdrv;
+    Chardev *cdrv;
     uint32_t vendorid = 0, productid = 0;
     char label[32];
     static int index;
@@ -564,7 +564,7 @@ static USBDevice *usb_serial_init(USBBus *bus, const char *filename)
 static USBDevice *usb_braille_init(USBBus *bus, const char *unused)
 {
     USBDevice *dev;
-    CharDriverState *cdrv;
+    Chardev *cdrv;
 
     cdrv = qemu_chr_new("braille", "braille");
     if (!cdrv)
diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c
index a657237..3359e6a 100644
--- a/hw/usb/redirect.c
+++ b/hw/usb/redirect.c
@@ -284,7 +284,7 @@ static gboolean usbredir_write_unblocked(GIOChannel *chan, GIOCondition cond,
 static int usbredir_write(void *priv, uint8_t *data, int count)
 {
     USBRedirDevice *dev = priv;
-    CharDriverState *chr = qemu_chr_fe_get_driver(&dev->cs);
+    Chardev *chr = qemu_chr_fe_get_driver(&dev->cs);
     int r;
 
     if (!chr->be_open) {
@@ -1430,7 +1430,7 @@ static void usbredir_cleanup_device_queues(USBRedirDevice *dev)
 static void usbredir_handle_destroy(USBDevice *udev)
 {
     USBRedirDevice *dev = USB_REDIRECT(udev);
-    CharDriverState *chr = qemu_chr_fe_get_driver(&dev->cs);
+    Chardev *chr = qemu_chr_fe_get_driver(&dev->cs);
 
     qemu_chr_fe_deinit(&dev->cs);
     qemu_chr_delete(chr);
diff --git a/include/hw/arm/exynos4210.h b/include/hw/arm/exynos4210.h
index 76bb6d4..d9e0801 100644
--- a/include/hw/arm/exynos4210.h
+++ b/include/hw/arm/exynos4210.h
@@ -131,7 +131,7 @@ void exynos4210_combiner_get_gpioin(Exynos4210Irq *irqs, DeviceState *dev,
 DeviceState *exynos4210_uart_create(hwaddr addr,
                                     int fifo_size,
                                     int channel,
-                                    CharDriverState *chr,
+                                    Chardev *chr,
                                     qemu_irq irq);
 
 #endif /* EXYNOS4210_H */
diff --git a/include/hw/arm/omap.h b/include/hw/arm/omap.h
index f25870b..cac1b2b 100644
--- a/include/hw/arm/omap.h
+++ b/include/hw/arm/omap.h
@@ -664,14 +664,14 @@ struct omap_uart_s;
 struct omap_uart_s *omap_uart_init(hwaddr base,
                 qemu_irq irq, omap_clk fclk, omap_clk iclk,
                 qemu_irq txdma, qemu_irq rxdma,
-                const char *label, CharDriverState *chr);
+                const char *label, Chardev *chr);
 struct omap_uart_s *omap2_uart_init(MemoryRegion *sysmem,
                 struct omap_target_agent_s *ta,
                 qemu_irq irq, omap_clk fclk, omap_clk iclk,
                 qemu_irq txdma, qemu_irq rxdma,
-                const char *label, CharDriverState *chr);
+                const char *label, Chardev *chr);
 void omap_uart_reset(struct omap_uart_s *s);
-void omap_uart_attach(struct omap_uart_s *s, CharDriverState *chr);
+void omap_uart_attach(struct omap_uart_s *s, Chardev *chr);
 
 struct omap_mpuio_s;
 qemu_irq *omap_mpuio_in_get(struct omap_mpuio_s *s);
diff --git a/include/hw/bt.h b/include/hw/bt.h
index 2fa22bd..b5e11d4 100644
--- a/include/hw/bt.h
+++ b/include/hw/bt.h
@@ -127,8 +127,8 @@ enum {
     csrhci_pin_wakeup,
     __csrhci_pins,
 };
-qemu_irq *csrhci_pins_get(CharDriverState *chr);
-CharDriverState *uart_hci_init(void);
+qemu_irq *csrhci_pins_get(Chardev *chr);
+Chardev *uart_hci_init(void);
 
 /* bt-l2cap.c */
 struct bt_l2cap_device_s;
diff --git a/include/hw/char/cadence_uart.h b/include/hw/char/cadence_uart.h
index ca75eb5..c836db4 100644
--- a/include/hw/char/cadence_uart.h
+++ b/include/hw/char/cadence_uart.h
@@ -51,7 +51,7 @@ typedef struct {
 
 static inline DeviceState *cadence_uart_create(hwaddr addr,
                                         qemu_irq irq,
-                                        CharDriverState *chr)
+                                        Chardev *chr)
 {
     DeviceState *dev;
     SysBusDevice *s;
diff --git a/include/hw/char/escc.h b/include/hw/char/escc.h
index 297e2eb..08ae122 100644
--- a/include/hw/char/escc.h
+++ b/include/hw/char/escc.h
@@ -5,7 +5,7 @@
 #define TYPE_ESCC "escc"
 #define ESCC_SIZE 4
 MemoryRegion *escc_init(hwaddr base, qemu_irq irqA, qemu_irq irqB,
-              CharDriverState *chrA, CharDriverState *chrB,
+              Chardev *chrA, Chardev *chrB,
               int clock, int it_shift);
 
 void slavio_serial_ms_kbd_init(hwaddr base, qemu_irq irq,
diff --git a/include/hw/char/pl011.h b/include/hw/char/pl011.h
index 0ca7c19..8364932 100644
--- a/include/hw/char/pl011.h
+++ b/include/hw/char/pl011.h
@@ -17,7 +17,7 @@
 
 static inline DeviceState *pl011_create(hwaddr addr,
                                         qemu_irq irq,
-                                        CharDriverState *chr)
+                                        Chardev *chr)
 {
     DeviceState *dev;
     SysBusDevice *s;
@@ -34,7 +34,7 @@ static inline DeviceState *pl011_create(hwaddr addr,
 
 static inline DeviceState *pl011_luminary_create(hwaddr addr,
                                                  qemu_irq irq,
-                                                 CharDriverState *chr)
+                                                 Chardev *chr)
 {
     DeviceState *dev;
     SysBusDevice *s;
diff --git a/include/hw/char/serial.h b/include/hw/char/serial.h
index c928d7d..daebb07 100644
--- a/include/hw/char/serial.h
+++ b/include/hw/char/serial.h
@@ -88,11 +88,11 @@ void serial_set_frequency(SerialState *s, uint32_t frequency);
 
 /* legacy pre qom */
 SerialState *serial_init(int base, qemu_irq irq, int baudbase,
-                         CharDriverState *chr, MemoryRegion *system_io);
+                         Chardev *chr, MemoryRegion *system_io);
 SerialState *serial_mm_init(MemoryRegion *address_space,
                             hwaddr base, int it_shift,
                             qemu_irq irq, int baudbase,
-                            CharDriverState *chr, enum device_endian end);
+                            Chardev *chr, enum device_endian end);
 
 /* serial-isa.c */
 #define TYPE_ISA_SERIAL "isa-serial"
diff --git a/include/hw/char/xilinx_uartlite.h b/include/hw/char/xilinx_uartlite.h
index 8b4fc54..634086b 100644
--- a/include/hw/char/xilinx_uartlite.h
+++ b/include/hw/char/xilinx_uartlite.h
@@ -17,7 +17,7 @@
 
 static inline DeviceState *xilinx_uartlite_create(hwaddr addr,
                                         qemu_irq irq,
-                                        CharDriverState *chr)
+                                        Chardev *chr)
 {
     DeviceState *dev;
     SysBusDevice *s;
diff --git a/include/hw/cris/etraxfs.h b/include/hw/cris/etraxfs.h
index 723a275..8da965a 100644
--- a/include/hw/cris/etraxfs.h
+++ b/include/hw/cris/etraxfs.h
@@ -48,7 +48,7 @@ etraxfs_eth_init(NICInfo *nd, hwaddr base, int phyaddr,
 
 static inline DeviceState *etraxfs_ser_create(hwaddr addr,
                                               qemu_irq irq,
-                                              CharDriverState *chr)
+                                              Chardev *chr)
 {
     DeviceState *dev;
     SysBusDevice *s;
diff --git a/include/hw/devices.h b/include/hw/devices.h
index c60bcab..7475b71 100644
--- a/include/hw/devices.h
+++ b/include/hw/devices.h
@@ -65,6 +65,6 @@ qemu_irq tc6393xb_l3v_get(TC6393xbState *s);
 /* sm501.c */
 void sm501_init(struct MemoryRegion *address_space_mem, uint32_t base,
                 uint32_t local_mem_bytes, qemu_irq irq,
-                CharDriverState *chr);
+                Chardev *chr);
 
 #endif
diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
index e34ab30..c016059 100644
--- a/include/hw/i386/pc.h
+++ b/include/hw/i386/pc.h
@@ -181,7 +181,7 @@ void parallel_hds_isa_init(ISABus *bus, int n);
 
 bool parallel_mm_init(MemoryRegion *address_space,
                       hwaddr base, int it_shift, qemu_irq irq,
-                      CharDriverState *chr);
+                      Chardev *chr);
 
 /* i8259.c */
 
diff --git a/include/hw/m68k/mcf.h b/include/hw/m68k/mcf.h
index bf43998..9a0bcfa 100644
--- a/include/hw/m68k/mcf.h
+++ b/include/hw/m68k/mcf.h
@@ -11,10 +11,10 @@ uint64_t mcf_uart_read(void *opaque, hwaddr addr,
                        unsigned size);
 void mcf_uart_write(void *opaque, hwaddr addr,
                     uint64_t val, unsigned size);
-void *mcf_uart_init(qemu_irq irq, CharDriverState *chr);
+void *mcf_uart_init(qemu_irq irq, Chardev *chr);
 void mcf_uart_mm_init(struct MemoryRegion *sysmem,
                       hwaddr base,
-                      qemu_irq irq, CharDriverState *chr);
+                      qemu_irq irq, Chardev *chr);
 
 /* mcf_intc.c */
 qemu_irq *mcf_intc_init(struct MemoryRegion *sysmem,
diff --git a/include/hw/ppc/spapr_vio.h b/include/hw/ppc/spapr_vio.h
index 14f5022..fc6f673 100644
--- a/include/hw/ppc/spapr_vio.h
+++ b/include/hw/ppc/spapr_vio.h
@@ -127,7 +127,7 @@ int spapr_vio_send_crq(VIOsPAPRDevice *dev, uint8_t *crq);
 
 VIOsPAPRDevice *vty_lookup(sPAPRMachineState *spapr, target_ulong reg);
 void vty_putchars(VIOsPAPRDevice *sdev, uint8_t *buf, int len);
-void spapr_vty_create(VIOsPAPRBus *bus, CharDriverState *chardev);
+void spapr_vty_create(VIOsPAPRBus *bus, Chardev *chardev);
 void spapr_vlan_create(VIOsPAPRBus *bus, NICInfo *nd);
 void spapr_vscsi_create(VIOsPAPRBus *bus);
 
diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h
index 306bbab..7ac3153 100644
--- a/include/hw/qdev-properties.h
+++ b/include/hw/qdev-properties.h
@@ -184,7 +184,7 @@ void qdev_prop_set_uint32(DeviceState *dev, const char *name, uint32_t value);
 void qdev_prop_set_int32(DeviceState *dev, const char *name, int32_t value);
 void qdev_prop_set_uint64(DeviceState *dev, const char *name, uint64_t value);
 void qdev_prop_set_string(DeviceState *dev, const char *name, const char *value);
-void qdev_prop_set_chr(DeviceState *dev, const char *name, CharDriverState *value);
+void qdev_prop_set_chr(DeviceState *dev, const char *name, Chardev *value);
 void qdev_prop_set_netdev(DeviceState *dev, const char *name, NetClientState *value);
 void qdev_prop_set_drive(DeviceState *dev, const char *name,
                          BlockBackend *value, Error **errp);
diff --git a/include/hw/sh4/sh.h b/include/hw/sh4/sh.h
index e59b9e7..767a2df 100644
--- a/include/hw/sh4/sh.h
+++ b/include/hw/sh4/sh.h
@@ -42,7 +42,7 @@ void tmu012_init(struct MemoryRegion *sysmem, hwaddr base,
 #define SH_SERIAL_FEAT_SCIF (1 << 0)
 void sh_serial_init(MemoryRegion *sysmem,
                     hwaddr base, int feat,
-		     uint32_t freq, CharDriverState *chr,
+                    uint32_t freq, Chardev *chr,
 		     qemu_irq eri_source,
 		     qemu_irq rxi_source,
 		     qemu_irq txi_source,
diff --git a/include/hw/sparc/grlib.h b/include/hw/sparc/grlib.h
index afbb9bc..61a345c 100644
--- a/include/hw/sparc/grlib.h
+++ b/include/hw/sparc/grlib.h
@@ -100,7 +100,7 @@ DeviceState *grlib_gptimer_create(hwaddr  base,
 
 static inline
 DeviceState *grlib_apbuart_create(hwaddr  base,
-                                  CharDriverState    *serial,
+                                  Chardev    *serial,
                                   qemu_irq            irq)
 {
     DeviceState *dev;
diff --git a/include/hw/xen/xen.h b/include/hw/xen/xen.h
index a8f3afb..09c2ce5 100644
--- a/include/hw/xen/xen.h
+++ b/include/hw/xen/xen.h
@@ -37,7 +37,7 @@ int xen_is_pirq_msi(uint32_t msi_data);
 
 qemu_irq *xen_interrupt_controller_init(void);
 
-void xenstore_store_pv_console_info(int i, struct CharDriverState *chr);
+void xenstore_store_pv_console_info(int i, struct Chardev *chr);
 
 void xen_hvm_init(PCMachineState *pcms, MemoryRegion **ram_memory);
 
diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h
index 8cc532e..e64b944 100644
--- a/include/monitor/monitor.h
+++ b/include/monitor/monitor.h
@@ -16,7 +16,7 @@ extern Monitor *cur_mon;
 
 bool monitor_cur_is_qmp(void);
 
-void monitor_init(CharDriverState *chr, int flags);
+void monitor_init(Chardev *chr, int flags);
 void monitor_cleanup(void);
 
 int monitor_suspend(Monitor *mon);
diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h
index 9a8bcbd..e95f28c 100644
--- a/include/qemu/typedefs.h
+++ b/include/qemu/typedefs.h
@@ -17,7 +17,7 @@ typedef struct BlockBackendRootState BlockBackendRootState;
 typedef struct BlockDriverState BlockDriverState;
 typedef struct BusClass BusClass;
 typedef struct BusState BusState;
-typedef struct CharDriverState CharDriverState;
+typedef struct Chardev Chardev;
 typedef struct CompatProperty CompatProperty;
 typedef struct CPUAddressSpace CPUAddressSpace;
 typedef struct CPUState CPUState;
diff --git a/include/sysemu/char.h b/include/sysemu/char.h
index 8bb94e5..3a82d80 100644
--- a/include/sysemu/char.h
+++ b/include/sysemu/char.h
@@ -76,9 +76,9 @@ typedef enum {
 } CharDriverFeature;
 
 /* This is the backend as seen by frontend, the actual backend is
- * CharDriverState */
+ * Chardev */
 typedef struct CharBackend {
-    CharDriverState *chr;
+    Chardev *chr;
     IOEventHandler *chr_event;
     IOCanReadHandler *chr_can_read;
     IOReadHandler *chr_read;
@@ -89,7 +89,7 @@ typedef struct CharBackend {
 
 typedef struct CharDriver CharDriver;
 
-struct CharDriverState {
+struct Chardev {
     const CharDriver *driver;
     QemuMutex chr_write_lock;
     CharBackend *be;
@@ -99,7 +99,7 @@ struct CharDriverState {
     int be_open;
     guint fd_in_tag;
     DECLARE_BITMAP(features, QEMU_CHAR_FEATURE_LAST);
-    QTAILQ_ENTRY(CharDriverState) next;
+    QTAILQ_ENTRY(Chardev) next;
 };
 
 /**
@@ -107,12 +107,12 @@ struct CharDriverState {
  * @backend: the common backend config
  * @errp: pointer to a NULL-initialized error object
  *
- * Allocate and initialize a new CharDriverState.
+ * Allocate and initialize a new Chardev.
  *
- * Returns: a newly allocated CharDriverState, or NULL on error.
+ * Returns: a newly allocated Chardev, or NULL on error.
  */
-CharDriverState *qemu_chr_alloc(const CharDriver *driver,
-                                ChardevCommon *backend, Error **errp);
+Chardev *qemu_chr_alloc(const CharDriver *driver,
+                        ChardevCommon *backend, Error **errp);
 
 /**
  * @qemu_chr_new_from_opts:
@@ -123,8 +123,8 @@ CharDriverState *qemu_chr_alloc(const CharDriver *driver,
  *
  * Returns: a new character backend
  */
-CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
-                                        Error **errp);
+Chardev *qemu_chr_new_from_opts(QemuOpts *opts,
+                                Error **errp);
 
 /**
  * @qemu_chr_parse_common:
@@ -146,7 +146,7 @@ void qemu_chr_parse_common(QemuOpts *opts, ChardevCommon *backend);
  *
  * Returns: a new character backend
  */
-CharDriverState *qemu_chr_new(const char *label, const char *filename);
+Chardev *qemu_chr_new(const char *label, const char *filename);
 
 
 /**
@@ -184,7 +184,7 @@ int qemu_chr_fe_wait_connected(CharBackend *be, Error **errp);
  *
  * Returns: a new character backend
  */
-CharDriverState *qemu_chr_new_noreplay(const char *label, const char *filename);
+Chardev *qemu_chr_new_noreplay(const char *label, const char *filename);
 
 /**
  * @qemu_chr_delete:
@@ -192,14 +192,14 @@ CharDriverState *qemu_chr_new_noreplay(const char *label, const char *filename);
  * Destroy a character backend and remove it from the list of
  * identified character backends.
  */
-void qemu_chr_delete(CharDriverState *chr);
+void qemu_chr_delete(Chardev *chr);
 
 /**
  * @qemu_chr_free:
  *
  * Destroy a character backend.
  */
-void qemu_chr_free(CharDriverState *chr);
+void qemu_chr_free(Chardev *chr);
 
 /**
  * @qemu_chr_fe_set_echo:
@@ -355,7 +355,7 @@ int qemu_chr_fe_set_msgfds(CharBackend *be, int *fds, int num);
  *
  * Returns: the number of bytes the front end can receive via @qemu_chr_be_write
  */
-int qemu_chr_be_can_write(CharDriverState *s);
+int qemu_chr_be_can_write(Chardev *s);
 
 /**
  * @qemu_chr_be_write:
@@ -367,7 +367,7 @@ int qemu_chr_be_can_write(CharDriverState *s);
  * @buf a buffer to receive data from the front end
  * @len the number of bytes to receive from the front end
  */
-void qemu_chr_be_write(CharDriverState *s, uint8_t *buf, int len);
+void qemu_chr_be_write(Chardev *s, uint8_t *buf, int len);
 
 /**
  * @qemu_chr_be_write_impl:
@@ -377,7 +377,7 @@ void qemu_chr_be_write(CharDriverState *s, uint8_t *buf, int len);
  * @buf a buffer to receive data from the front end
  * @len the number of bytes to receive from the front end
  */
-void qemu_chr_be_write_impl(CharDriverState *s, uint8_t *buf, int len);
+void qemu_chr_be_write_impl(Chardev *s, uint8_t *buf, int len);
 
 /**
  * @qemu_chr_be_event:
@@ -386,7 +386,7 @@ void qemu_chr_be_write_impl(CharDriverState *s, uint8_t *buf, int len);
  *
  * @event the event to send
  */
-void qemu_chr_be_event(CharDriverState *s, int event);
+void qemu_chr_be_event(Chardev *s, int event);
 
 /**
  * @qemu_chr_fe_init:
@@ -397,7 +397,7 @@ void qemu_chr_be_event(CharDriverState *s, int event);
  *
  * Returns: false on error.
  */
-bool qemu_chr_fe_init(CharBackend *b, CharDriverState *s, Error **errp);
+bool qemu_chr_fe_init(CharBackend *b, Chardev *s, Error **errp);
 
 /**
  * @qemu_chr_fe_get_driver:
@@ -405,7 +405,7 @@ bool qemu_chr_fe_init(CharBackend *b, CharDriverState *s, Error **errp);
  * Returns the driver associated with a CharBackend or NULL if no
  * associated CharDriver.
  */
-CharDriverState *qemu_chr_fe_get_driver(CharBackend *be);
+Chardev *qemu_chr_fe_get_driver(CharBackend *be);
 
 /**
  * @qemu_chr_fe_deinit:
@@ -450,27 +450,27 @@ void qemu_chr_fe_set_handlers(CharBackend *b,
  */
 void qemu_chr_fe_take_focus(CharBackend *b);
 
-void qemu_chr_be_generic_open(CharDriverState *s);
+void qemu_chr_be_generic_open(Chardev *s);
 void qemu_chr_fe_accept_input(CharBackend *be);
-int qemu_chr_add_client(CharDriverState *s, int fd);
-CharDriverState *qemu_chr_find(const char *name);
+int qemu_chr_add_client(Chardev *s, int fd);
+Chardev *qemu_chr_find(const char *name);
 
 /**
  * @qemu_chr_get_kind:
  *
  * Returns the kind of char backend, or -1 if unspecified.
  */
-ChardevBackendKind qemu_chr_get_kind(const CharDriverState *chr);
+ChardevBackendKind qemu_chr_get_kind(const Chardev *chr);
 
-static inline bool qemu_chr_is_ringbuf(const CharDriverState *chr)
+static inline bool qemu_chr_is_ringbuf(const Chardev *chr)
 {
     return qemu_chr_get_kind(chr) == CHARDEV_BACKEND_KIND_RINGBUF ||
         qemu_chr_get_kind(chr) == CHARDEV_BACKEND_KIND_MEMORY;
 }
 
-bool qemu_chr_has_feature(CharDriverState *chr,
+bool qemu_chr_has_feature(Chardev *chr,
                           CharDriverFeature feature);
-void qemu_chr_set_feature(CharDriverState *chr,
+void qemu_chr_set_feature(Chardev *chr,
                           CharDriverFeature feature);
 QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename);
 
@@ -478,29 +478,29 @@ struct CharDriver {
     ChardevBackendKind kind;
     const char *alias;
     void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp);
-    CharDriverState *(*create)(const CharDriver *driver,
-                               const char *id,
-                               ChardevBackend *backend,
-                               ChardevReturn *ret, bool *be_opened,
-                               Error **errp);
+    Chardev *(*create)(const CharDriver *driver,
+                       const char *id,
+                       ChardevBackend *backend,
+                       ChardevReturn *ret, bool *be_opened,
+                       Error **errp);
     size_t instance_size;
 
-    int (*chr_write)(struct CharDriverState *s, const uint8_t *buf, int len);
-    int (*chr_sync_read)(struct CharDriverState *s,
+    int (*chr_write)(struct Chardev *s, const uint8_t *buf, int len);
+    int (*chr_sync_read)(struct Chardev *s,
                          const uint8_t *buf, int len);
-    GSource *(*chr_add_watch)(struct CharDriverState *s, GIOCondition cond);
-    void (*chr_update_read_handler)(struct CharDriverState *s,
+    GSource *(*chr_add_watch)(struct Chardev *s, GIOCondition cond);
+    void (*chr_update_read_handler)(struct Chardev *s,
                                     GMainContext *context);
-    int (*chr_ioctl)(struct CharDriverState *s, int cmd, void *arg);
-    int (*get_msgfds)(struct CharDriverState *s, int* fds, int num);
-    int (*set_msgfds)(struct CharDriverState *s, int *fds, int num);
-    int (*chr_add_client)(struct CharDriverState *chr, int fd);
-    int (*chr_wait_connected)(struct CharDriverState *chr, Error **errp);
-    void (*chr_free)(struct CharDriverState *chr);
-    void (*chr_disconnect)(struct CharDriverState *chr);
-    void (*chr_accept_input)(struct CharDriverState *chr);
-    void (*chr_set_echo)(struct CharDriverState *chr, bool echo);
-    void (*chr_set_fe_open)(struct CharDriverState *chr, int fe_open);
+    int (*chr_ioctl)(struct Chardev *s, int cmd, void *arg);
+    int (*get_msgfds)(struct Chardev *s, int* fds, int num);
+    int (*set_msgfds)(struct Chardev *s, int *fds, int num);
+    int (*chr_add_client)(struct Chardev *chr, int fd);
+    int (*chr_wait_connected)(struct Chardev *chr, Error **errp);
+    void (*chr_free)(struct Chardev *chr);
+    void (*chr_disconnect)(struct Chardev *chr);
+    void (*chr_accept_input)(struct Chardev *chr);
+    void (*chr_set_echo)(struct Chardev *chr, bool echo);
+    void (*chr_set_fe_open)(struct Chardev *chr, int fe_open);
 };
 
 void register_char_driver(const CharDriver *driver);
@@ -509,7 +509,7 @@ extern int term_escape_char;
 
 
 /* console.c */
-typedef CharDriverState *(VcHandler)(ChardevVC *vc, Error **errp);
+typedef Chardev *(VcHandler)(ChardevVC *vc, Error **errp);
 void register_vc_handler(VcHandler *handler);
 
 #endif
diff --git a/include/sysemu/replay.h b/include/sysemu/replay.h
index 740b425..7aad20b 100644
--- a/include/sysemu/replay.h
+++ b/include/sysemu/replay.h
@@ -128,9 +128,9 @@ uint64_t blkreplay_next_id(void);
 /* Character device */
 
 /*! Registers char driver to save it's events */
-void replay_register_char_driver(struct CharDriverState *chr);
+void replay_register_char_driver(struct Chardev *chr);
 /*! Saves write to char device event to the log */
-void replay_chr_be_write(struct CharDriverState *s, uint8_t *buf, int len);
+void replay_chr_be_write(struct Chardev *s, uint8_t *buf, int len);
 /*! Writes char write return value to the replay log. */
 void replay_char_write_event_save(int res, int offset);
 /*! Reads char write return value from the replay log. */
diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h
index 4ef2eb0..4d50694 100644
--- a/include/sysemu/sysemu.h
+++ b/include/sysemu/sysemu.h
@@ -190,13 +190,13 @@ void hmp_pcie_aer_inject_error(Monitor *mon, const QDict *qdict);
 
 #define MAX_SERIAL_PORTS 4
 
-extern CharDriverState *serial_hds[MAX_SERIAL_PORTS];
+extern Chardev *serial_hds[MAX_SERIAL_PORTS];
 
 /* parallel ports */
 
 #define MAX_PARALLEL_PORTS 3
 
-extern CharDriverState *parallel_hds[MAX_PARALLEL_PORTS];
+extern Chardev *parallel_hds[MAX_PARALLEL_PORTS];
 
 void hmp_usb_add(Monitor *mon, const QDict *qdict);
 void hmp_usb_del(Monitor *mon, const QDict *qdict);
diff --git a/include/ui/gtk.h b/include/ui/gtk.h
index b3b5005..47ffddb 100644
--- a/include/ui/gtk.h
+++ b/include/ui/gtk.h
@@ -64,7 +64,7 @@ typedef struct VirtualVteConsole {
     GtkWidget *box;
     GtkWidget *scrollbar;
     GtkWidget *terminal;
-    CharDriverState *chr;
+    Chardev *chr;
     bool echo;
 } VirtualVteConsole;
 #endif
diff --git a/include/ui/qemu-spice.h b/include/ui/qemu-spice.h
index 75e1239..52a9f88 100644
--- a/include/ui/qemu-spice.h
+++ b/include/ui/qemu-spice.h
@@ -51,7 +51,7 @@ int qemu_spice_migrate_info(const char *hostname, int port, int tls_port,
 #if SPICE_SERVER_VERSION >= 0x000c02
 void qemu_spice_register_ports(void);
 #else
-static inline CharDriverState *qemu_chr_open_spice_port(const char *name)
+static inline Chardev *qemu_chr_open_spice_port(const char *name)
 { return NULL; }
 #endif
 
diff --git a/monitor.c b/monitor.c
index 24add02..065f659 100644
--- a/monitor.c
+++ b/monitor.c
@@ -3195,7 +3195,7 @@ static void ringbuf_completion(ReadLineState *rs, const char *str)
         ChardevInfo *chr_info = list->value;
 
         if (!strncmp(chr_info->label, str, len)) {
-            CharDriverState *chr = qemu_chr_find(chr_info->label);
+            Chardev *chr = qemu_chr_find(chr_info->label);
             if (chr && qemu_chr_is_ringbuf(chr)) {
                 readline_add_completion(rs, chr_info->label);
             }
@@ -3985,7 +3985,7 @@ static void __attribute__((constructor)) monitor_lock_init(void)
     qemu_mutex_init(&monitor_lock);
 }
 
-void monitor_init(CharDriverState *chr, int flags)
+void monitor_init(Chardev *chr, int flags)
 {
     static int is_first_init = 1;
     Monitor *mon;
diff --git a/net/colo-compare.c b/net/colo-compare.c
index 9bfc736..4962976 100644
--- a/net/colo-compare.c
+++ b/net/colo-compare.c
@@ -564,7 +564,7 @@ static void compare_sec_rs_finalize(SocketReadState *sec_rs)
  * Return 0 is success.
  * Return 1 is failed.
  */
-static int find_and_check_chardev(CharDriverState **chr,
+static int find_and_check_chardev(Chardev **chr,
                                   char *chr_name,
                                   Error **errp)
 {
@@ -611,7 +611,7 @@ static void check_old_packet_regular(void *opaque)
 static void colo_compare_complete(UserCreatable *uc, Error **errp)
 {
     CompareState *s = COLO_COMPARE(uc);
-    CharDriverState *chr;
+    Chardev *chr;
     char thread_name[64];
     static int compare_id;
 
diff --git a/net/filter-mirror.c b/net/filter-mirror.c
index b7d6456..aa0aa98 100644
--- a/net/filter-mirror.c
+++ b/net/filter-mirror.c
@@ -191,7 +191,7 @@ static void filter_redirector_cleanup(NetFilterState *nf)
 static void filter_mirror_setup(NetFilterState *nf, Error **errp)
 {
     MirrorState *s = FILTER_MIRROR(nf);
-    CharDriverState *chr;
+    Chardev *chr;
 
     if (!s->outdev) {
         error_setg(errp, "filter mirror needs 'outdev' "
@@ -220,7 +220,7 @@ static void redirector_rs_finalize(SocketReadState *rs)
 static void filter_redirector_setup(NetFilterState *nf, Error **errp)
 {
     MirrorState *s = FILTER_REDIRECTOR(nf);
-    CharDriverState *chr;
+    Chardev *chr;
 
     if (!s->indev && !s->outdev) {
         error_setg(errp, "filter redirector needs 'indev' or "
diff --git a/net/slirp.c b/net/slirp.c
index bcd1c5f..f97ec23 100644
--- a/net/slirp.c
+++ b/net/slirp.c
@@ -748,7 +748,7 @@ static int slirp_guestfwd(SlirpState *s, const char *config_str,
         }
     } else {
         Error *err = NULL;
-        CharDriverState *chr = qemu_chr_new(buf, p);
+        Chardev *chr = qemu_chr_new(buf, p);
 
         if (!chr) {
             error_report("could not open guest forwarding device '%s'", buf);
diff --git a/net/vhost-user.c b/net/vhost-user.c
index 7aff77e..b0f0ab6 100644
--- a/net/vhost-user.c
+++ b/net/vhost-user.c
@@ -195,7 +195,7 @@ static void net_vhost_user_event(void *opaque, int event)
     const char *name = opaque;
     NetClientState *ncs[MAX_QUEUE_NUM];
     VhostUserState *s;
-    CharDriverState *chr;
+    Chardev *chr;
     Error *err = NULL;
     int queues;
 
@@ -232,7 +232,7 @@ static void net_vhost_user_event(void *opaque, int event)
 }
 
 static int net_vhost_user_init(NetClientState *peer, const char *device,
-                               const char *name, CharDriverState *chr,
+                               const char *name, Chardev *chr,
                                int queues)
 {
     Error *err = NULL;
@@ -274,10 +274,10 @@ static int net_vhost_user_init(NetClientState *peer, const char *device,
     return 0;
 }
 
-static CharDriverState *net_vhost_claim_chardev(
+static Chardev *net_vhost_claim_chardev(
     const NetdevVhostUserOptions *opts, Error **errp)
 {
-    CharDriverState *chr = qemu_chr_find(opts->chardev);
+    Chardev *chr = qemu_chr_find(opts->chardev);
 
     if (chr == NULL) {
         error_setg(errp, "chardev \"%s\" not found", opts->chardev);
@@ -324,7 +324,7 @@ int net_init_vhost_user(const Netdev *netdev, const char *name,
 {
     int queues;
     const NetdevVhostUserOptions *vhost_user_opts;
-    CharDriverState *chr;
+    Chardev *chr;
 
     assert(netdev->type == NET_CLIENT_DRIVER_VHOST_USER);
     vhost_user_opts = &netdev->u.vhost_user;
diff --git a/qemu-char.c b/qemu-char.c
index dfd1b4c..f828e61 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -89,7 +89,7 @@
 #define READ_RETRIES 10
 #define TCP_MAX_FDS 16
 
-typedef struct MuxDriver MuxDriver;
+typedef struct MuxChardev MuxChardev;
 
 /***********************************************************/
 /* Socket address helpers */
@@ -157,19 +157,19 @@ static char *sockaddr_to_str(struct sockaddr_storage *ss, socklen_t ss_len,
 /***********************************************************/
 /* character device */
 
-static QTAILQ_HEAD(CharDriverStateHead, CharDriverState) chardevs =
+static QTAILQ_HEAD(ChardevHead, Chardev) chardevs =
     QTAILQ_HEAD_INITIALIZER(chardevs);
 
-static void qemu_chr_free_common(CharDriverState *chr);
+static void qemu_chr_free_common(Chardev *chr);
 
-CharDriverState *qemu_chr_alloc(const CharDriver *driver,
+Chardev *qemu_chr_alloc(const CharDriver *driver,
                                 ChardevCommon *backend, Error **errp)
 {
-    CharDriverState *chr;
+    Chardev *chr;
 
     assert(driver);
     assert(driver->chr_write);
-    assert(driver->instance_size >= sizeof(CharDriverState));
+    assert(driver->instance_size >= sizeof(Chardev));
 
     chr = g_malloc0(driver->instance_size);
     qemu_mutex_init(&chr->chr_write_lock);
@@ -197,7 +197,7 @@ CharDriverState *qemu_chr_alloc(const CharDriver *driver,
     return chr;
 }
 
-void qemu_chr_be_event(CharDriverState *s, int event)
+void qemu_chr_be_event(Chardev *s, int event)
 {
     CharBackend *be = s->be;
 
@@ -218,7 +218,7 @@ void qemu_chr_be_event(CharDriverState *s, int event)
     be->chr_event(be->opaque, event);
 }
 
-void qemu_chr_be_generic_open(CharDriverState *s)
+void qemu_chr_be_generic_open(Chardev *s)
 {
     qemu_chr_be_event(s, CHR_EVENT_OPENED);
 }
@@ -226,7 +226,7 @@ void qemu_chr_be_generic_open(CharDriverState *s)
 
 /* Not reporting errors from writing to logfile, as logs are
  * defined to be "best effort" only */
-static void qemu_chr_fe_write_log(CharDriverState *s,
+static void qemu_chr_fe_write_log(Chardev *s,
                                   const uint8_t *buf, size_t len)
 {
     size_t done = 0;
@@ -251,7 +251,8 @@ static void qemu_chr_fe_write_log(CharDriverState *s,
     }
 }
 
-static int qemu_chr_fe_write_buffer(CharDriverState *s, const uint8_t *buf, int len, int *offset)
+static int qemu_chr_fe_write_buffer(Chardev *s,
+                                    const uint8_t *buf, int len, int *offset)
 {
     int res = 0;
     *offset = 0;
@@ -279,14 +280,14 @@ static int qemu_chr_fe_write_buffer(CharDriverState *s, const uint8_t *buf, int
     return res;
 }
 
-static bool qemu_chr_replay(CharDriverState *chr)
+static bool qemu_chr_replay(Chardev *chr)
 {
     return qemu_chr_has_feature(chr, QEMU_CHAR_FEATURE_REPLAY);
 }
 
 int qemu_chr_fe_write(CharBackend *be, const uint8_t *buf, int len)
 {
-    CharDriverState *s = be->chr;
+    Chardev *s = be->chr;
     int ret;
 
     if (!s) {
@@ -317,7 +318,7 @@ int qemu_chr_fe_write(CharBackend *be, const uint8_t *buf, int len)
     return ret;
 }
 
-static int qemu_chr_write_all(CharDriverState *s, const uint8_t *buf, int len)
+static int qemu_chr_write_all(Chardev *s, const uint8_t *buf, int len)
 {
     int offset;
     int res;
@@ -343,7 +344,7 @@ static int qemu_chr_write_all(CharDriverState *s, const uint8_t *buf, int len)
 
 int qemu_chr_fe_write_all(CharBackend *be, const uint8_t *buf, int len)
 {
-    CharDriverState *s = be->chr;
+    Chardev *s = be->chr;
 
     if (!s) {
         return 0;
@@ -354,7 +355,7 @@ int qemu_chr_fe_write_all(CharBackend *be, const uint8_t *buf, int len)
 
 int qemu_chr_fe_read_all(CharBackend *be, uint8_t *buf, int len)
 {
-    CharDriverState *s = be->chr;
+    Chardev *s = be->chr;
     int offset = 0, counter = 10;
     int res;
 
@@ -400,7 +401,7 @@ int qemu_chr_fe_read_all(CharBackend *be, uint8_t *buf, int len)
 
 int qemu_chr_fe_ioctl(CharBackend *be, int cmd, void *arg)
 {
-    CharDriverState *s = be->chr;
+    Chardev *s = be->chr;
     int res;
 
     if (!s || !s->driver->chr_ioctl || qemu_chr_replay(s)) {
@@ -412,7 +413,7 @@ int qemu_chr_fe_ioctl(CharBackend *be, int cmd, void *arg)
     return res;
 }
 
-int qemu_chr_be_can_write(CharDriverState *s)
+int qemu_chr_be_can_write(Chardev *s)
 {
     CharBackend *be = s->be;
 
@@ -423,7 +424,7 @@ int qemu_chr_be_can_write(CharDriverState *s)
     return be->chr_can_read(be->opaque);
 }
 
-void qemu_chr_be_write_impl(CharDriverState *s, uint8_t *buf, int len)
+void qemu_chr_be_write_impl(Chardev *s, uint8_t *buf, int len)
 {
     CharBackend *be = s->be;
 
@@ -432,7 +433,7 @@ void qemu_chr_be_write_impl(CharDriverState *s, uint8_t *buf, int len)
     }
 }
 
-void qemu_chr_be_write(CharDriverState *s, uint8_t *buf, int len)
+void qemu_chr_be_write(Chardev *s, uint8_t *buf, int len)
 {
     if (qemu_chr_replay(s)) {
         if (replay_mode == REPLAY_MODE_PLAY) {
@@ -446,7 +447,7 @@ void qemu_chr_be_write(CharDriverState *s, uint8_t *buf, int len)
 
 int qemu_chr_fe_get_msgfd(CharBackend *be)
 {
-    CharDriverState *s = be->chr;
+    Chardev *s = be->chr;
     int fd;
     int res = (qemu_chr_fe_get_msgfds(be, &fd, 1) == 1) ? fd : -1;
     if (s && qemu_chr_replay(s)) {
@@ -459,7 +460,7 @@ int qemu_chr_fe_get_msgfd(CharBackend *be)
 
 int qemu_chr_fe_get_msgfds(CharBackend *be, int *fds, int len)
 {
-    CharDriverState *s = be->chr;
+    Chardev *s = be->chr;
 
     if (!s) {
         return -1;
@@ -470,7 +471,7 @@ int qemu_chr_fe_get_msgfds(CharBackend *be, int *fds, int len)
 
 int qemu_chr_fe_set_msgfds(CharBackend *be, int *fds, int num)
 {
-    CharDriverState *s = be->chr;
+    Chardev *s = be->chr;
 
     if (!s) {
         return -1;
@@ -479,14 +480,14 @@ int qemu_chr_fe_set_msgfds(CharBackend *be, int *fds, int num)
     return s->driver->set_msgfds ? s->driver->set_msgfds(s, fds, num) : -1;
 }
 
-int qemu_chr_add_client(CharDriverState *s, int fd)
+int qemu_chr_add_client(Chardev *s, int fd)
 {
     return s->driver->chr_add_client ? s->driver->chr_add_client(s, fd) : -1;
 }
 
 void qemu_chr_fe_accept_input(CharBackend *be)
 {
-    CharDriverState *s = be->chr;
+    Chardev *s = be->chr;
 
     if (!s) {
         return;
@@ -510,23 +511,23 @@ void qemu_chr_fe_printf(CharBackend *be, const char *fmt, ...)
     va_end(ap);
 }
 
-static void remove_fd_in_watch(CharDriverState *chr);
-static void mux_chr_set_handlers(CharDriverState *chr, GMainContext *context);
-static void mux_set_focus(CharDriverState *chr, int focus);
+static void remove_fd_in_watch(Chardev *chr);
+static void mux_chr_set_handlers(Chardev *chr, GMainContext *context);
+static void mux_set_focus(Chardev *chr, int focus);
 
-static int null_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+static int null_chr_write(Chardev *chr, const uint8_t *buf, int len)
 {
     return len;
 }
 
-static CharDriverState *qemu_chr_open_null(const CharDriver *driver,
-                                           const char *id,
-                                           ChardevBackend *backend,
-                                           ChardevReturn *ret,
-                                           bool *be_opened,
-                                           Error **errp)
+static Chardev *qemu_chr_open_null(const CharDriver *driver,
+                                   const char *id,
+                                   ChardevBackend *backend,
+                                   ChardevReturn *ret,
+                                   bool *be_opened,
+                                   Error **errp)
 {
-    CharDriverState *chr;
+    Chardev *chr;
     ChardevCommon *common = backend->u.null.data;
 
     chr = qemu_chr_alloc(driver, common, errp);
@@ -538,7 +539,7 @@ static CharDriverState *qemu_chr_open_null(const CharDriver *driver,
 }
 
 static const CharDriver null_driver = {
-    .instance_size = sizeof(CharDriverState),
+    .instance_size = sizeof(Chardev),
     .kind = CHARDEV_BACKEND_KIND_NULL,
     .create = qemu_chr_open_null,
     .chr_write = null_chr_write,
@@ -548,8 +549,8 @@ static const CharDriver null_driver = {
 #define MAX_MUX 4
 #define MUX_BUFFER_SIZE 32	/* Must be a power of 2.  */
 #define MUX_BUFFER_MASK (MUX_BUFFER_SIZE - 1)
-struct MuxDriver {
-    CharDriverState parent;
+struct MuxChardev {
+    Chardev parent;
     CharBackend *backends[MAX_MUX];
     CharBackend chr;
     int focus;
@@ -564,15 +565,15 @@ struct MuxDriver {
     int cons[MAX_MUX];
     int timestamps;
 
-    /* Protected by the CharDriverState chr_write_lock.  */
+    /* Protected by the Chardev chr_write_lock.  */
     int linestart;
     int64_t timestamps_start;
 };
 
 /* Called with chr_write_lock held.  */
-static int mux_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+static int mux_chr_write(Chardev *chr, const uint8_t *buf, int len)
 {
-    MuxDriver *d = (MuxDriver *)chr;
+    MuxChardev *d = (MuxChardev *)chr;
     int ret;
     if (!d->timestamps) {
         ret = qemu_chr_fe_write(&d->chr, buf, len);
@@ -624,7 +625,7 @@ static const char * const mux_help[] = {
 };
 
 int term_escape_char = 0x01; /* ctrl-a is used for escape */
-static void mux_print_help(CharDriverState *chr)
+static void mux_print_help(Chardev *chr)
 {
     int i, j;
     char ebuf[15] = "Escape-Char";
@@ -651,7 +652,7 @@ static void mux_print_help(CharDriverState *chr)
     }
 }
 
-static void mux_chr_send_event(MuxDriver *d, int mux_nr, int event)
+static void mux_chr_send_event(MuxChardev *d, int mux_nr, int event)
 {
     CharBackend *be = d->backends[mux_nr];
 
@@ -660,7 +661,7 @@ static void mux_chr_send_event(MuxDriver *d, int mux_nr, int event)
     }
 }
 
-static int mux_proc_byte(CharDriverState *chr, MuxDriver *d, int ch)
+static int mux_proc_byte(Chardev *chr, MuxChardev *d, int ch)
 {
     if (d->term_got_escape) {
         d->term_got_escape = 0;
@@ -704,9 +705,9 @@ static int mux_proc_byte(CharDriverState *chr, MuxDriver *d, int ch)
     return 0;
 }
 
-static void mux_chr_accept_input(CharDriverState *chr)
+static void mux_chr_accept_input(Chardev *chr)
 {
-    MuxDriver *d = (MuxDriver *)chr;
+    MuxChardev *d = (MuxChardev *)chr;
     int m = d->focus;
     CharBackend *be = d->backends[m];
 
@@ -719,7 +720,7 @@ static void mux_chr_accept_input(CharDriverState *chr)
 
 static int mux_chr_can_read(void *opaque)
 {
-    MuxDriver *d = opaque;
+    MuxChardev *d = opaque;
     int m = d->focus;
     CharBackend *be = d->backends[m];
 
@@ -736,8 +737,8 @@ static int mux_chr_can_read(void *opaque)
 
 static void mux_chr_read(void *opaque, const uint8_t *buf, int size)
 {
-    CharDriverState *chr = opaque;
-    MuxDriver *d = opaque;
+    Chardev *chr = opaque;
+    MuxChardev *d = opaque;
     int m = d->focus;
     CharBackend *be = d->backends[m];
     int i;
@@ -759,7 +760,7 @@ static bool muxes_realized;
 
 static void mux_chr_event(void *opaque, int event)
 {
-    MuxDriver *d = opaque;
+    MuxChardev *d = opaque;
     int i;
 
     if (!muxes_realized) {
@@ -785,11 +786,11 @@ static void mux_chr_event(void *opaque, int event)
  */
 static void muxes_realize_done(Notifier *notifier, void *unused)
 {
-    CharDriverState *chr;
+    Chardev *chr;
 
     QTAILQ_FOREACH(chr, &chardevs, next) {
         if (qemu_chr_get_kind(chr) == CHARDEV_BACKEND_KIND_MUX) {
-            MuxDriver *d = (MuxDriver *)chr;
+            MuxChardev *d = (MuxChardev *)chr;
             int i;
 
             /* send OPENED to all already-attached FEs */
@@ -809,10 +810,10 @@ static Notifier muxes_realize_notify = {
     .notify = muxes_realize_done,
 };
 
-static GSource *mux_chr_add_watch(CharDriverState *s, GIOCondition cond)
+static GSource *mux_chr_add_watch(Chardev *s, GIOCondition cond)
 {
-    MuxDriver *d = (MuxDriver *)s;
-    CharDriverState *chr = qemu_chr_fe_get_driver(&d->chr);
+    MuxChardev *d = (MuxChardev *)s;
+    Chardev *chr = qemu_chr_fe_get_driver(&d->chr);
 
     if (!chr->driver->chr_add_watch) {
         return NULL;
@@ -821,9 +822,9 @@ static GSource *mux_chr_add_watch(CharDriverState *s, GIOCondition cond)
     return chr->driver->chr_add_watch(chr, cond);
 }
 
-static void mux_chr_free(struct CharDriverState *chr)
+static void mux_chr_free(struct Chardev *chr)
 {
-    MuxDriver *d = (MuxDriver *)chr;
+    MuxChardev *d = (MuxChardev *)chr;
     int i;
 
     for (i = 0; i < d->mux_cnt; i++) {
@@ -835,9 +836,9 @@ static void mux_chr_free(struct CharDriverState *chr)
     qemu_chr_fe_deinit(&d->chr);
 }
 
-static void mux_chr_set_handlers(CharDriverState *chr, GMainContext *context)
+static void mux_chr_set_handlers(Chardev *chr, GMainContext *context)
 {
-    MuxDriver *d = (MuxDriver *)chr;
+    MuxChardev *d = (MuxChardev *)chr;
 
     /* Fix up the real driver with mux routines */
     qemu_chr_fe_set_handlers(&d->chr,
@@ -848,9 +849,9 @@ static void mux_chr_set_handlers(CharDriverState *chr, GMainContext *context)
                              context, true);
 }
 
-static void mux_set_focus(CharDriverState *chr, int focus)
+static void mux_set_focus(Chardev *chr, int focus)
 {
-    MuxDriver *d = (MuxDriver *)chr;
+    MuxChardev *d = (MuxChardev *)chr;
 
     assert(focus >= 0);
     assert(focus < d->mux_cnt);
@@ -864,16 +865,16 @@ static void mux_set_focus(CharDriverState *chr, int focus)
     mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_IN);
 }
 
-static CharDriverState *qemu_chr_open_mux(const CharDriver *driver,
-                                          const char *id,
-                                          ChardevBackend *backend,
-                                          ChardevReturn *ret,
-                                          bool *be_opened,
-                                          Error **errp)
+static Chardev *qemu_chr_open_mux(const CharDriver *driver,
+                                  const char *id,
+                                  ChardevBackend *backend,
+                                  ChardevReturn *ret,
+                                  bool *be_opened,
+                                  Error **errp)
 {
     ChardevMux *mux = backend->u.mux.data;
-    CharDriverState *chr, *drv;
-    MuxDriver *d;
+    Chardev *chr, *drv;
+    MuxChardev *d;
     ChardevCommon *common = qapi_ChardevMux_base(mux);
 
     drv = qemu_chr_find(mux->chardev);
@@ -886,7 +887,7 @@ static CharDriverState *qemu_chr_open_mux(const CharDriver *driver,
     if (!chr) {
         return NULL;
     }
-    d = (MuxDriver *)chr;
+    d = (MuxChardev *)chr;
     d->focus = -1;
     /* only default to opened state if we've realized the initial
      * set of muxes
@@ -900,17 +901,17 @@ static CharDriverState *qemu_chr_open_mux(const CharDriver *driver,
     return chr;
 }
 
-CharDriverState *qemu_chr_fe_get_driver(CharBackend *be)
+Chardev *qemu_chr_fe_get_driver(CharBackend *be)
 {
     return be->chr;
 }
 
-bool qemu_chr_fe_init(CharBackend *b, CharDriverState *s, Error **errp)
+bool qemu_chr_fe_init(CharBackend *b, Chardev *s, Error **errp)
 {
     int tag = 0;
 
     if (qemu_chr_get_kind(s) == CHARDEV_BACKEND_KIND_MUX) {
-        MuxDriver *d = (MuxDriver *)s;
+        MuxChardev *d = (MuxChardev *)s;
 
         if (d->mux_cnt >= MAX_MUX) {
             goto unavailable;
@@ -934,10 +935,10 @@ unavailable:
     return false;
 }
 
-static bool qemu_chr_is_busy(CharDriverState *s)
+static bool qemu_chr_is_busy(Chardev *s)
 {
     if (qemu_chr_get_kind(s) == CHARDEV_BACKEND_KIND_MUX) {
-        MuxDriver *d = (MuxDriver *)s;
+        MuxChardev *d = (MuxChardev *)s;
         return d->mux_cnt >= 0;
     } else {
         return s->be != NULL;
@@ -954,7 +955,7 @@ void qemu_chr_fe_deinit(CharBackend *b)
             b->chr->be = NULL;
         }
         if (qemu_chr_get_kind(b->chr) == CHARDEV_BACKEND_KIND_MUX) {
-            MuxDriver *d = (MuxDriver *)b->chr;
+            MuxChardev *d = (MuxChardev *)b->chr;
             d->backends[b->tag] = NULL;
         }
         b->chr = NULL;
@@ -969,7 +970,7 @@ void qemu_chr_fe_set_handlers(CharBackend *b,
                               GMainContext *context,
                               bool set_open)
 {
-    CharDriverState *s;
+    Chardev *s;
     int fe_open;
 
     s = b->chr;
@@ -1096,7 +1097,7 @@ static GSourceFuncs io_watch_poll_funcs = {
 };
 
 /* Can only be used for read */
-static guint io_add_watch_poll(CharDriverState *chr,
+static guint io_add_watch_poll(Chardev *chr,
                                QIOChannel *ioc,
                                IOCanReadHandler *fd_can_read,
                                QIOChannelFunc fd_read,
@@ -1144,7 +1145,7 @@ static void io_remove_watch_poll(guint tag)
     g_source_destroy(&iwp->parent);
 }
 
-static void remove_fd_in_watch(CharDriverState *chr)
+static void remove_fd_in_watch(Chardev *chr)
 {
     if (chr->fd_in_tag) {
         io_remove_watch_poll(chr->fd_in_tag);
@@ -1193,25 +1194,25 @@ static int io_channel_send(QIOChannel *ioc, const void *buf, size_t len)
 }
 
 
-typedef struct FDCharDriver {
-    CharDriverState parent;
-    CharDriverState *chr;
+typedef struct FDChardev {
+    Chardev parent;
+    Chardev *chr;
     QIOChannel *ioc_in, *ioc_out;
     int max_size;
-} FDCharDriver;
+} FDChardev;
 
 /* Called with chr_write_lock held.  */
-static int fd_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+static int fd_chr_write(Chardev *chr, const uint8_t *buf, int len)
 {
-    FDCharDriver *s = (FDCharDriver *)chr;
+    FDChardev *s = (FDChardev *)chr;
 
     return io_channel_send(s->ioc_out, buf, len);
 }
 
 static gboolean fd_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
 {
-    CharDriverState *chr = opaque;
-    FDCharDriver *s = opaque;
+    Chardev *chr = opaque;
+    FDChardev *s = opaque;
     int len;
     uint8_t buf[READ_BUF_LEN];
     ssize_t ret;
@@ -1240,23 +1241,23 @@ static gboolean fd_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
 
 static int fd_chr_read_poll(void *opaque)
 {
-    CharDriverState *chr = opaque;
-    FDCharDriver *s = opaque;
+    Chardev *chr = opaque;
+    FDChardev *s = opaque;
 
     s->max_size = qemu_chr_be_can_write(chr);
     return s->max_size;
 }
 
-static GSource *fd_chr_add_watch(CharDriverState *chr, GIOCondition cond)
+static GSource *fd_chr_add_watch(Chardev *chr, GIOCondition cond)
 {
-    FDCharDriver *s = (FDCharDriver *)chr;
+    FDChardev *s = (FDChardev *)chr;
     return qio_channel_create_watch(s->ioc_out, cond);
 }
 
-static void fd_chr_update_read_handler(CharDriverState *chr,
+static void fd_chr_update_read_handler(Chardev *chr,
                                        GMainContext *context)
 {
-    FDCharDriver *s = (FDCharDriver *)chr;
+    FDChardev *s = (FDChardev *)chr;
 
     remove_fd_in_watch(chr);
     if (s->ioc_in) {
@@ -1267,9 +1268,9 @@ static void fd_chr_update_read_handler(CharDriverState *chr,
     }
 }
 
-static void fd_chr_free(struct CharDriverState *chr)
+static void fd_chr_free(struct Chardev *chr)
 {
-    FDCharDriver *s = (FDCharDriver *)chr;
+    FDChardev *s = (FDChardev *)chr;
 
     remove_fd_in_watch(chr);
     if (s->ioc_in) {
@@ -1283,19 +1284,19 @@ static void fd_chr_free(struct CharDriverState *chr)
 }
 
 /* open a character device to a unix fd */
-static CharDriverState *qemu_chr_open_fd(const CharDriver *driver,
-                                         int fd_in, int fd_out,
-                                         ChardevCommon *backend, Error **errp)
+static Chardev *qemu_chr_open_fd(const CharDriver *driver,
+                                 int fd_in, int fd_out,
+                                 ChardevCommon *backend, Error **errp)
 {
-    CharDriverState *chr;
-    FDCharDriver *s;
+    Chardev *chr;
+    FDChardev *s;
     char *name;
 
     chr = qemu_chr_alloc(driver, backend, errp);
     if (!chr) {
         return NULL;
     }
-    s = (FDCharDriver *)chr;
+    s = (FDChardev *)chr;
     s->ioc_in = QIO_CHANNEL(qio_channel_file_new_fd(fd_in));
     name = g_strdup_printf("chardev-file-in-%s", chr->label);
     qio_channel_set_name(QIO_CHANNEL(s->ioc_in), name);
@@ -1310,12 +1311,12 @@ static CharDriverState *qemu_chr_open_fd(const CharDriver *driver,
     return chr;
 }
 
-static CharDriverState *qemu_chr_open_pipe(const CharDriver *driver,
-                                           const char *id,
-                                           ChardevBackend *backend,
-                                           ChardevReturn *ret,
-                                           bool *be_opened,
-                                           Error **errp)
+static Chardev *qemu_chr_open_pipe(const CharDriver *driver,
+                                   const char *id,
+                                   ChardevBackend *backend,
+                                   ChardevReturn *ret,
+                                   bool *be_opened,
+                                   Error **errp)
 {
     ChardevHostdev *opts = backend->u.pipe.data;
     int fd_in, fd_out;
@@ -1352,7 +1353,7 @@ static bool stdio_in_use;
 static bool stdio_allow_signal;
 static bool stdio_echo_state;
 
-static void qemu_chr_set_echo_stdio(CharDriverState *chr, bool echo);
+static void qemu_chr_set_echo_stdio(Chardev *chr, bool echo);
 
 static void term_exit(void)
 {
@@ -1366,7 +1367,7 @@ static void term_stdio_handler(int sig)
     qemu_chr_set_echo_stdio(NULL, stdio_echo_state);
 }
 
-static void qemu_chr_set_echo_stdio(CharDriverState *chr, bool echo)
+static void qemu_chr_set_echo_stdio(Chardev *chr, bool echo)
 {
     struct termios tty;
 
@@ -1388,21 +1389,21 @@ static void qemu_chr_set_echo_stdio(CharDriverState *chr, bool echo)
     tcsetattr (0, TCSANOW, &tty);
 }
 
-static void qemu_chr_free_stdio(struct CharDriverState *chr)
+static void qemu_chr_free_stdio(struct Chardev *chr)
 {
     term_exit();
     fd_chr_free(chr);
 }
 
-static CharDriverState *qemu_chr_open_stdio(const CharDriver *driver,
-                                            const char *id,
-                                            ChardevBackend *backend,
-                                            ChardevReturn *ret,
-                                            bool *be_opened,
-                                            Error **errp)
+static Chardev *qemu_chr_open_stdio(const CharDriver *driver,
+                                    const char *id,
+                                    ChardevBackend *backend,
+                                    ChardevReturn *ret,
+                                    bool *be_opened,
+                                    Error **errp)
 {
     ChardevStdio *opts = backend->u.stdio.data;
-    CharDriverState *chr;
+    Chardev *chr;
     struct sigaction act;
     ChardevCommon *common = qapi_ChardevStdio_base(opts);
 
@@ -1446,23 +1447,23 @@ static CharDriverState *qemu_chr_open_stdio(const CharDriver *driver,
 #define HAVE_CHARDEV_PTY 1
 
 typedef struct {
-    CharDriverState parent;
+    Chardev parent;
     QIOChannel *ioc;
     int read_bytes;
 
-    /* Protected by the CharDriverState chr_write_lock.  */
+    /* Protected by the Chardev chr_write_lock.  */
     int connected;
     guint timer_tag;
     guint open_tag;
-} PtyCharDriver;
+} PtyChardev;
 
-static void pty_chr_update_read_handler_locked(CharDriverState *chr);
-static void pty_chr_state(CharDriverState *chr, int connected);
+static void pty_chr_update_read_handler_locked(Chardev *chr);
+static void pty_chr_state(Chardev *chr, int connected);
 
 static gboolean pty_chr_timer(gpointer opaque)
 {
-    struct CharDriverState *chr = opaque;
-    PtyCharDriver *s = opaque;
+    struct Chardev *chr = opaque;
+    PtyChardev *s = opaque;
 
     qemu_mutex_lock(&chr->chr_write_lock);
     s->timer_tag = 0;
@@ -1476,9 +1477,9 @@ static gboolean pty_chr_timer(gpointer opaque)
 }
 
 /* Called with chr_write_lock held.  */
-static void pty_chr_rearm_timer(CharDriverState *chr, int ms)
+static void pty_chr_rearm_timer(Chardev *chr, int ms)
 {
-    PtyCharDriver *s = (PtyCharDriver *)chr;
+    PtyChardev *s = (PtyChardev *)chr;
     char *name;
 
     if (s->timer_tag) {
@@ -1498,9 +1499,9 @@ static void pty_chr_rearm_timer(CharDriverState *chr, int ms)
 }
 
 /* Called with chr_write_lock held.  */
-static void pty_chr_update_read_handler_locked(CharDriverState *chr)
+static void pty_chr_update_read_handler_locked(Chardev *chr)
 {
-    PtyCharDriver *s = (PtyCharDriver *)chr;
+    PtyChardev *s = (PtyChardev *)chr;
     GPollFD pfd;
     int rc;
     QIOChannelFile *fioc = QIO_CHANNEL_FILE(s->ioc);
@@ -1520,7 +1521,7 @@ static void pty_chr_update_read_handler_locked(CharDriverState *chr)
     }
 }
 
-static void pty_chr_update_read_handler(CharDriverState *chr,
+static void pty_chr_update_read_handler(Chardev *chr,
                                         GMainContext *context)
 {
     qemu_mutex_lock(&chr->chr_write_lock);
@@ -1529,9 +1530,9 @@ static void pty_chr_update_read_handler(CharDriverState *chr,
 }
 
 /* Called with chr_write_lock held.  */
-static int pty_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+static int pty_chr_write(Chardev *chr, const uint8_t *buf, int len)
 {
-    PtyCharDriver *s = (PtyCharDriver *)chr;
+    PtyChardev *s = (PtyChardev *)chr;
 
     if (!s->connected) {
         /* guest sends data, check for (re-)connect */
@@ -1543,9 +1544,9 @@ static int pty_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
     return io_channel_send(s->ioc, buf, len);
 }
 
-static GSource *pty_chr_add_watch(CharDriverState *chr, GIOCondition cond)
+static GSource *pty_chr_add_watch(Chardev *chr, GIOCondition cond)
 {
-    PtyCharDriver *s = (PtyCharDriver *)chr;
+    PtyChardev *s = (PtyChardev *)chr;
     if (!s->connected) {
         return NULL;
     }
@@ -1554,8 +1555,8 @@ static GSource *pty_chr_add_watch(CharDriverState *chr, GIOCondition cond)
 
 static int pty_chr_read_poll(void *opaque)
 {
-    CharDriverState *chr = opaque;
-    PtyCharDriver *s = opaque;
+    Chardev *chr = opaque;
+    PtyChardev *s = opaque;
 
     s->read_bytes = qemu_chr_be_can_write(chr);
     return s->read_bytes;
@@ -1563,8 +1564,8 @@ static int pty_chr_read_poll(void *opaque)
 
 static gboolean pty_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
 {
-    CharDriverState *chr = opaque;
-    PtyCharDriver *s = opaque;
+    Chardev *chr = opaque;
+    PtyChardev *s = opaque;
     gsize len;
     uint8_t buf[READ_BUF_LEN];
     ssize_t ret;
@@ -1588,8 +1589,8 @@ static gboolean pty_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
 
 static gboolean qemu_chr_be_generic_open_func(gpointer opaque)
 {
-    CharDriverState *chr = opaque;
-    PtyCharDriver *s = opaque;
+    Chardev *chr = opaque;
+    PtyChardev *s = opaque;
 
     s->open_tag = 0;
     qemu_chr_be_generic_open(chr);
@@ -1597,9 +1598,9 @@ static gboolean qemu_chr_be_generic_open_func(gpointer opaque)
 }
 
 /* Called with chr_write_lock held.  */
-static void pty_chr_state(CharDriverState *chr, int connected)
+static void pty_chr_state(Chardev *chr, int connected)
 {
-    PtyCharDriver *s = (PtyCharDriver *)chr;
+    PtyChardev *s = (PtyChardev *)chr;
 
     if (!connected) {
         if (s->open_tag) {
@@ -1631,9 +1632,9 @@ static void pty_chr_state(CharDriverState *chr, int connected)
     }
 }
 
-static void pty_chr_free(struct CharDriverState *chr)
+static void pty_chr_free(struct Chardev *chr)
 {
-    PtyCharDriver *s = (PtyCharDriver *)chr;
+    PtyChardev *s = (PtyChardev *)chr;
 
     qemu_mutex_lock(&chr->chr_write_lock);
     pty_chr_state(chr, 0);
@@ -1646,15 +1647,15 @@ static void pty_chr_free(struct CharDriverState *chr)
     qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
 }
 
-static CharDriverState *qemu_chr_open_pty(const CharDriver *driver,
-                                          const char *id,
-                                          ChardevBackend *backend,
-                                          ChardevReturn *ret,
-                                          bool *be_opened,
-                                          Error **errp)
+static Chardev *qemu_chr_open_pty(const CharDriver *driver,
+                                  const char *id,
+                                  ChardevBackend *backend,
+                                  ChardevReturn *ret,
+                                  bool *be_opened,
+                                  Error **errp)
 {
-    CharDriverState *chr;
-    PtyCharDriver *s;
+    Chardev *chr;
+    PtyChardev *s;
     int master_fd, slave_fd;
     char pty_name[PATH_MAX];
     ChardevCommon *common = backend->u.pty.data;
@@ -1682,7 +1683,7 @@ static CharDriverState *qemu_chr_open_pty(const CharDriver *driver,
     fprintf(stderr, "char device redirected to %s (label %s)\n",
             pty_name, id);
 
-    s = (PtyCharDriver *)chr;
+    s = (PtyChardev *)chr;
     s->ioc = QIO_CHANNEL(qio_channel_file_new_fd(master_fd));
     name = g_strdup_printf("chardev-pty-%s", chr->label);
     qio_channel_set_name(QIO_CHANNEL(s->ioc), name);
@@ -1694,7 +1695,7 @@ static CharDriverState *qemu_chr_open_pty(const CharDriver *driver,
 }
 
 static const CharDriver pty_driver = {
-    .instance_size = sizeof(PtyCharDriver),
+    .instance_size = sizeof(PtyChardev),
     .kind = CHARDEV_BACKEND_KIND_PTY,
     .create = qemu_chr_open_pty,
     .chr_write = pty_chr_write,
@@ -1818,9 +1819,9 @@ static void tty_serial_init(int fd, int speed,
     tcsetattr (fd, TCSANOW, &tty);
 }
 
-static int tty_serial_ioctl(CharDriverState *chr, int cmd, void *arg)
+static int tty_serial_ioctl(Chardev *chr, int cmd, void *arg)
 {
-    FDCharDriver *s = (FDCharDriver *)chr;
+    FDChardev *s = (FDChardev *)chr;
     QIOChannelFile *fioc = QIO_CHANNEL_FILE(s->ioc_in);
 
     switch(cmd) {
@@ -1888,7 +1889,7 @@ static int tty_serial_ioctl(CharDriverState *chr, int cmd, void *arg)
     return 0;
 }
 
-static void qemu_chr_free_tty(CharDriverState *chr)
+static void qemu_chr_free_tty(Chardev *chr)
 {
     fd_chr_free(chr);
 }
@@ -1899,12 +1900,12 @@ static void qemu_chr_free_tty(CharDriverState *chr)
 #define HAVE_CHARDEV_PARPORT 1
 
 typedef struct {
-    CharDriverState parent;
+    Chardev parent;
     int fd;
     int mode;
-} ParallelCharDriver;
+} ParallelChardev;
 
-static int pp_hw_mode(ParallelCharDriver *s, uint16_t mode)
+static int pp_hw_mode(ParallelChardev *s, uint16_t mode)
 {
     if (s->mode != mode) {
 	int m = mode;
@@ -1915,9 +1916,9 @@ static int pp_hw_mode(ParallelCharDriver *s, uint16_t mode)
     return 1;
 }
 
-static int pp_ioctl(CharDriverState *chr, int cmd, void *arg)
+static int pp_ioctl(Chardev *chr, int cmd, void *arg)
 {
-    ParallelCharDriver *drv = (ParallelCharDriver *)chr;
+    ParallelChardev *drv = (ParallelChardev *)chr;
     int fd = drv->fd;
     uint8_t b;
 
@@ -1996,9 +1997,9 @@ static int pp_ioctl(CharDriverState *chr, int cmd, void *arg)
     return 0;
 }
 
-static void pp_free(CharDriverState *chr)
+static void pp_free(Chardev *chr)
 {
-    ParallelCharDriver *drv = (ParallelCharDriver *)chr;
+    ParallelChardev *drv = (ParallelChardev *)chr;
     int fd = drv->fd;
 
     pp_hw_mode(drv, IEEE1284_MODE_COMPAT);
@@ -2007,14 +2008,14 @@ static void pp_free(CharDriverState *chr)
     qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
 }
 
-static CharDriverState *qemu_chr_open_pp_fd(const CharDriver *driver,
-                                            int fd,
-                                            ChardevCommon *backend,
-                                            bool *be_opened,
-                                            Error **errp)
+static Chardev *qemu_chr_open_pp_fd(const CharDriver *driver,
+                                    int fd,
+                                    ChardevCommon *backend,
+                                    bool *be_opened,
+                                    Error **errp)
 {
-    CharDriverState *chr;
-    ParallelCharDriver *drv;
+    Chardev *chr;
+    ParallelChardev *drv;
 
     if (ioctl(fd, PPCLAIM) < 0) {
         error_setg_errno(errp, errno, "not a parallel port");
@@ -2027,7 +2028,7 @@ static CharDriverState *qemu_chr_open_pp_fd(const CharDriver *driver,
         return NULL;
     }
 
-    drv = (ParallelCharDriver *)chr;
+    drv = (ParallelChardev *)chr;
     drv->fd = fd;
     drv->mode = IEEE1284_MODE_COMPAT;
 
@@ -2040,13 +2041,13 @@ static CharDriverState *qemu_chr_open_pp_fd(const CharDriver *driver,
 #define HAVE_CHARDEV_PARPORT 1
 
 typedef struct {
-    CharDriverState parent;
+    Chardev parent;
     int fd;
-} ParallelCharDriver;
+} ParallelChardev;
 
-static int pp_ioctl(CharDriverState *chr, int cmd, void *arg)
+static int pp_ioctl(Chardev *chr, int cmd, void *arg)
 {
-    ParallelCharDriver *drv = (ParallelCharDriver *)chr;
+    ParallelChardev *drv = (ParallelChardev *)chr;
     uint8_t b;
 
     switch (cmd) {
@@ -2086,20 +2087,20 @@ static int pp_ioctl(CharDriverState *chr, int cmd, void *arg)
     return 0;
 }
 
-static CharDriverState *qemu_chr_open_pp_fd(const CharDriver *driver,
-                                            int fd,
-                                            ChardevCommon *backend,
-                                            bool *be_opened,
-                                            Error **errp)
+static Chardev *qemu_chr_open_pp_fd(const CharDriver *driver,
+                                    int fd,
+                                    ChardevCommon *backend,
+                                    bool *be_opened,
+                                    Error **errp)
 {
-    CharDriverState *chr;
-    ParallelCharDriver *drv;
+    Chardev *chr;
+    ParallelChardev *drv;
 
     chr = qemu_chr_alloc(driver, backend, errp);
     if (!chr) {
         return NULL;
     }
-    drv = (ParallelCharDriver *)chr;
+    drv = (ParallelChardev *)chr;
     drv->fd = fd;
     *be_opened = false;
     return chr;
@@ -2111,25 +2112,25 @@ static CharDriverState *qemu_chr_open_pp_fd(const CharDriver *driver,
 #define HAVE_CHARDEV_SERIAL 1
 
 typedef struct {
-    CharDriverState parent;
+    Chardev parent;
     int max_size;
     HANDLE hcom, hrecv, hsend;
     OVERLAPPED orecv;
     BOOL fpipe;
     DWORD len;
 
-    /* Protected by the CharDriverState chr_write_lock.  */
+    /* Protected by the Chardev chr_write_lock.  */
     OVERLAPPED osend;
-} WinCharState;
+} WinChardev;
 
 typedef struct {
-    CharDriverState parent;
+    Chardev parent;
     HANDLE  hStdIn;
     HANDLE  hInputReadyEvent;
     HANDLE  hInputDoneEvent;
     HANDLE  hInputThread;
     uint8_t win_stdio_buf;
-} WinStdioCharState;
+} WinStdioChardev;
 
 #define NSENDBUF 2048
 #define NRECVBUF 2048
@@ -2139,9 +2140,9 @@ typedef struct {
 static int win_chr_poll(void *opaque);
 static int win_chr_pipe_poll(void *opaque);
 
-static void win_chr_free(CharDriverState *chr)
+static void win_chr_free(Chardev *chr)
 {
-    WinCharState *s = (WinCharState *)chr;
+    WinChardev *s = (WinChardev *)chr;
 
     if (s->hsend) {
         CloseHandle(s->hsend);
@@ -2163,9 +2164,9 @@ static void win_chr_free(CharDriverState *chr)
     qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
 }
 
-static int win_chr_init(CharDriverState *chr, const char *filename, Error **errp)
+static int win_chr_init(Chardev *chr, const char *filename, Error **errp)
 {
-    WinCharState *s = (WinCharState *)chr;
+    WinChardev *s = (WinChardev *)chr;
     COMMCONFIG comcfg;
     COMMTIMEOUTS cto = { 0, 0, 0, 0, 0};
     COMSTAT comstat;
@@ -2231,9 +2232,9 @@ static int win_chr_init(CharDriverState *chr, const char *filename, Error **errp
 }
 
 /* Called with chr_write_lock held.  */
-static int win_chr_write(CharDriverState *chr, const uint8_t *buf, int len1)
+static int win_chr_write(Chardev *chr, const uint8_t *buf, int len1)
 {
-    WinCharState *s = (WinCharState *)chr;
+    WinChardev *s = (WinChardev *)chr;
     DWORD len, ret, size, err;
 
     len = len1;
@@ -2265,17 +2266,17 @@ static int win_chr_write(CharDriverState *chr, const uint8_t *buf, int len1)
     return len1 - len;
 }
 
-static int win_chr_read_poll(CharDriverState *chr)
+static int win_chr_read_poll(Chardev *chr)
 {
-    WinCharState *s = (WinCharState *)chr;
+    WinChardev *s = (WinChardev *)chr;
 
     s->max_size = qemu_chr_be_can_write(chr);
     return s->max_size;
 }
 
-static void win_chr_readfile(CharDriverState *chr)
+static void win_chr_readfile(Chardev *chr)
 {
-    WinCharState *s = (WinCharState *)chr;
+    WinChardev *s = (WinChardev *)chr;
     int ret, err;
     uint8_t buf[READ_BUF_LEN];
     DWORD size;
@@ -2295,9 +2296,9 @@ static void win_chr_readfile(CharDriverState *chr)
     }
 }
 
-static void win_chr_read(CharDriverState *chr)
+static void win_chr_read(Chardev *chr)
 {
-    WinCharState *s = (WinCharState *)chr;
+    WinChardev *s = (WinChardev *)chr;
 
     if (s->len > s->max_size)
         s->len = s->max_size;
@@ -2309,8 +2310,8 @@ static void win_chr_read(CharDriverState *chr)
 
 static int win_chr_poll(void *opaque)
 {
-    CharDriverState *chr = opaque;
-    WinCharState *s = opaque;
+    Chardev *chr = opaque;
+    WinChardev *s = opaque;
     COMSTAT status;
     DWORD comerr;
 
@@ -2326,8 +2327,8 @@ static int win_chr_poll(void *opaque)
 
 static int win_chr_pipe_poll(void *opaque)
 {
-    CharDriverState *chr = opaque;
-    WinCharState *s = opaque;
+    Chardev *chr = opaque;
+    WinChardev *s = opaque;
     DWORD size;
 
     PeekNamedPipe(s->hcom, NULL, 0, NULL, &size, NULL);
@@ -2340,10 +2341,10 @@ static int win_chr_pipe_poll(void *opaque)
     return 0;
 }
 
-static int win_chr_pipe_init(CharDriverState *chr, const char *filename,
+static int win_chr_pipe_init(Chardev *chr, const char *filename,
                              Error **errp)
 {
-    WinCharState *s = (WinCharState *)chr;
+    WinChardev *s = (WinChardev *)chr;
     OVERLAPPED ov;
     int ret;
     DWORD size;
@@ -2405,16 +2406,16 @@ static int win_chr_pipe_init(CharDriverState *chr, const char *filename,
 }
 
 
-static CharDriverState *qemu_chr_open_pipe(const CharDriver *driver,
-                                           const char *id,
-                                           ChardevBackend *backend,
-                                           ChardevReturn *ret,
-                                           bool *be_opened,
-                                           Error **errp)
+static Chardev *qemu_chr_open_pipe(const CharDriver *driver,
+                                   const char *id,
+                                   ChardevBackend *backend,
+                                   ChardevReturn *ret,
+                                   bool *be_opened,
+                                   Error **errp)
 {
     ChardevHostdev *opts = backend->u.pipe.data;
     const char *filename = opts->device;
-    CharDriverState *chr;
+    Chardev *chr;
     ChardevCommon *common = qapi_ChardevHostdev_base(opts);
 
     chr = qemu_chr_alloc(driver, common, errp);
@@ -2429,29 +2430,29 @@ static CharDriverState *qemu_chr_open_pipe(const CharDriver *driver,
     return chr;
 }
 
-static CharDriverState *qemu_chr_open_win_file(const CharDriver *driver,
-                                               HANDLE fd_out,
-                                               ChardevCommon *backend,
-                                               Error **errp)
+static Chardev *qemu_chr_open_win_file(const CharDriver *driver,
+                                       HANDLE fd_out,
+                                       ChardevCommon *backend,
+                                       Error **errp)
 {
-    CharDriverState *chr;
-    WinCharState *s;
+    Chardev *chr;
+    WinChardev *s;
 
     chr = qemu_chr_alloc(driver, backend, errp);
     if (!chr) {
         return NULL;
     }
-    s = (WinCharState *)chr;
+    s = (WinChardev *)chr;
     s->hcom = fd_out;
     return chr;
 }
 
-static CharDriverState *qemu_chr_open_win_con(const CharDriver *driver,
-                                              const char *id,
-                                              ChardevBackend *backend,
-                                              ChardevReturn *ret,
-                                              bool *be_opened,
-                                              Error **errp)
+static Chardev *qemu_chr_open_win_con(const CharDriver *driver,
+                                      const char *id,
+                                      ChardevBackend *backend,
+                                      ChardevReturn *ret,
+                                      bool *be_opened,
+                                      Error **errp)
 {
     ChardevCommon *common = backend->u.console.data;
     return qemu_chr_open_win_file(driver,
@@ -2460,13 +2461,13 @@ static CharDriverState *qemu_chr_open_win_con(const CharDriver *driver,
 }
 
 static const CharDriver console_driver = {
-    .instance_size = sizeof(WinCharState),
+    .instance_size = sizeof(WinChardev),
     .kind = CHARDEV_BACKEND_KIND_CONSOLE,
     .create = qemu_chr_open_win_con,
     .chr_write = win_chr_write,
 };
 
-static int win_stdio_write(CharDriverState *chr, const uint8_t *buf, int len)
+static int win_stdio_write(Chardev *chr, const uint8_t *buf, int len)
 {
     HANDLE  hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
     DWORD   dwSize;
@@ -2487,8 +2488,8 @@ static int win_stdio_write(CharDriverState *chr, const uint8_t *buf, int len)
 
 static void win_stdio_wait_func(void *opaque)
 {
-    CharDriverState   *chr   = opaque;
-    WinStdioCharState *stdio = opaque;
+    Chardev   *chr   = opaque;
+    WinStdioChardev *stdio = opaque;
     INPUT_RECORD       buf[4];
     int                ret;
     DWORD              dwSize;
@@ -2521,7 +2522,7 @@ static void win_stdio_wait_func(void *opaque)
 
 static DWORD WINAPI win_stdio_thread(LPVOID param)
 {
-    WinStdioCharState *stdio = param;
+    WinStdioChardev *stdio = param;
     int                ret;
     DWORD              dwSize;
 
@@ -2559,8 +2560,8 @@ static DWORD WINAPI win_stdio_thread(LPVOID param)
 
 static void win_stdio_thread_wait_func(void *opaque)
 {
-    CharDriverState   *chr   = opaque;
-    WinStdioCharState *stdio = opaque;
+    Chardev   *chr   = opaque;
+    WinStdioChardev *stdio = opaque;
 
     if (qemu_chr_be_can_write(chr)) {
         qemu_chr_be_write(chr, &stdio->win_stdio_buf, 1);
@@ -2569,9 +2570,9 @@ static void win_stdio_thread_wait_func(void *opaque)
     SetEvent(stdio->hInputDoneEvent);
 }
 
-static void qemu_chr_set_echo_win_stdio(CharDriverState *chr, bool echo)
+static void qemu_chr_set_echo_win_stdio(Chardev *chr, bool echo)
 {
-    WinStdioCharState *stdio  = (WinStdioCharState *)chr;
+    WinStdioChardev *stdio  = (WinStdioChardev *)chr;
     DWORD              dwMode = 0;
 
     GetConsoleMode(stdio->hStdIn, &dwMode);
@@ -2583,9 +2584,9 @@ static void qemu_chr_set_echo_win_stdio(CharDriverState *chr, bool echo)
     }
 }
 
-static void win_stdio_free(CharDriverState *chr)
+static void win_stdio_free(Chardev *chr)
 {
-    WinStdioCharState *stdio = (WinStdioCharState *)chr;
+    WinStdioChardev *stdio = (WinStdioChardev *)chr;
 
     if (stdio->hInputReadyEvent != INVALID_HANDLE_VALUE) {
         CloseHandle(stdio->hInputReadyEvent);
@@ -2598,15 +2599,15 @@ static void win_stdio_free(CharDriverState *chr)
     }
 }
 
-static CharDriverState *qemu_chr_open_stdio(const CharDriver *driver,
-                                            const char *id,
-                                            ChardevBackend *backend,
-                                            ChardevReturn *ret,
-                                            bool *be_opened,
-                                            Error **errp)
+static Chardev *qemu_chr_open_stdio(const CharDriver *driver,
+                                    const char *id,
+                                    ChardevBackend *backend,
+                                    ChardevReturn *ret,
+                                    bool *be_opened,
+                                    Error **errp)
 {
-    CharDriverState   *chr;
-    WinStdioCharState *stdio;
+    Chardev   *chr;
+    WinStdioChardev *stdio;
     DWORD              dwMode;
     int                is_console = 0;
     ChardevCommon *common = qapi_ChardevStdio_base(backend->u.stdio.data);
@@ -2615,7 +2616,7 @@ static CharDriverState *qemu_chr_open_stdio(const CharDriver *driver,
     if (!chr) {
         return NULL;
     }
-    stdio = (WinStdioCharState *)chr;
+    stdio = (WinStdioChardev *)chr;
 
     stdio->hStdIn = GetStdHandle(STD_INPUT_HANDLE);
     if (stdio->hStdIn == INVALID_HANDLE_VALUE) {
@@ -2684,18 +2685,18 @@ err1:
 /* UDP Net console */
 
 typedef struct {
-    CharDriverState parent;
+    Chardev parent;
     QIOChannel *ioc;
     uint8_t buf[READ_BUF_LEN];
     int bufcnt;
     int bufptr;
     int max_size;
-} NetCharDriver;
+} NetChardev;
 
 /* Called with chr_write_lock held.  */
-static int udp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+static int udp_chr_write(Chardev *chr, const uint8_t *buf, int len)
 {
-    NetCharDriver *s = (NetCharDriver *)chr;
+    NetChardev *s = (NetChardev *)chr;
 
     return qio_channel_write(
         s->ioc, (const char *)buf, len, NULL);
@@ -2703,8 +2704,8 @@ static int udp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
 
 static int udp_chr_read_poll(void *opaque)
 {
-    CharDriverState *chr = opaque;
-    NetCharDriver *s = opaque;
+    Chardev *chr = opaque;
+    NetChardev *s = opaque;
 
     s->max_size = qemu_chr_be_can_write(chr);
 
@@ -2721,8 +2722,8 @@ static int udp_chr_read_poll(void *opaque)
 
 static gboolean udp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
 {
-    CharDriverState *chr = opaque;
-    NetCharDriver *s = opaque;
+    Chardev *chr = opaque;
+    NetChardev *s = opaque;
     ssize_t ret;
 
     if (s->max_size == 0) {
@@ -2746,10 +2747,10 @@ static gboolean udp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
     return TRUE;
 }
 
-static void udp_chr_update_read_handler(CharDriverState *chr,
+static void udp_chr_update_read_handler(Chardev *chr,
                                         GMainContext *context)
 {
-    NetCharDriver *s = (NetCharDriver *)chr;
+    NetChardev *s = (NetChardev *)chr;
 
     remove_fd_in_watch(chr);
     if (s->ioc) {
@@ -2760,9 +2761,9 @@ static void udp_chr_update_read_handler(CharDriverState *chr,
     }
 }
 
-static void udp_chr_free(CharDriverState *chr)
+static void udp_chr_free(Chardev *chr)
 {
-    NetCharDriver *s = (NetCharDriver *)chr;
+    NetChardev *s = (NetChardev *)chr;
 
     remove_fd_in_watch(chr);
     if (s->ioc) {
@@ -2775,7 +2776,7 @@ static void udp_chr_free(CharDriverState *chr)
 /* TCP Net console */
 
 typedef struct {
-    CharDriverState parent;
+    Chardev parent;
     QIOChannel *ioc; /* Client I/O channel */
     QIOChannelSocket *sioc; /* Client master channel */
     QIOChannelSocket *listen_ioc;
@@ -2798,13 +2799,13 @@ typedef struct {
     guint reconnect_timer;
     int64_t reconnect_time;
     bool connect_err_reported;
-} TCPCharDriver;
+} TCPChardev;
 
 static gboolean socket_reconnect_timeout(gpointer opaque);
 
-static void qemu_chr_socket_restart_timer(CharDriverState *chr)
+static void qemu_chr_socket_restart_timer(Chardev *chr)
 {
-    TCPCharDriver *s = (TCPCharDriver *)chr;
+    TCPChardev *s = (TCPChardev *)chr;
     char *name;
 
     assert(s->connected == 0);
@@ -2815,10 +2816,10 @@ static void qemu_chr_socket_restart_timer(CharDriverState *chr)
     g_free(name);
 }
 
-static void check_report_connect_error(CharDriverState *chr,
+static void check_report_connect_error(Chardev *chr,
                                        Error *err)
 {
-    TCPCharDriver *s = (TCPCharDriver *)chr;
+    TCPChardev *s = (TCPChardev *)chr;
 
     if (!s->connect_err_reported) {
         error_report("Unable to connect character device %s: %s",
@@ -2833,9 +2834,9 @@ static gboolean tcp_chr_accept(QIOChannel *chan,
                                void *opaque);
 
 /* Called with chr_write_lock held.  */
-static int tcp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+static int tcp_chr_write(Chardev *chr, const uint8_t *buf, int len)
 {
-    TCPCharDriver *s = (TCPCharDriver *)chr;
+    TCPChardev *s = (TCPChardev *)chr;
 
     if (s->connected) {
         int ret =  io_channel_send_full(s->ioc, buf, len,
@@ -2858,8 +2859,8 @@ static int tcp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
 
 static int tcp_chr_read_poll(void *opaque)
 {
-    CharDriverState *chr = opaque;
-    TCPCharDriver *s = opaque;
+    Chardev *chr = opaque;
+    TCPChardev *s = opaque;
     if (!s->connected)
         return 0;
     s->max_size = qemu_chr_be_can_write(chr);
@@ -2868,8 +2869,8 @@ static int tcp_chr_read_poll(void *opaque)
 
 #define IAC 255
 #define IAC_BREAK 243
-static void tcp_chr_process_IAC_bytes(CharDriverState *chr,
-                                      TCPCharDriver *s,
+static void tcp_chr_process_IAC_bytes(Chardev *chr,
+                                      TCPChardev *s,
                                       uint8_t *buf, int *size)
 {
     /* Handle any telnet client's basic IAC options to satisfy char by
@@ -2916,9 +2917,9 @@ static void tcp_chr_process_IAC_bytes(CharDriverState *chr,
     *size = j;
 }
 
-static int tcp_get_msgfds(CharDriverState *chr, int *fds, int num)
+static int tcp_get_msgfds(Chardev *chr, int *fds, int num)
 {
-    TCPCharDriver *s = (TCPCharDriver *)chr;
+    TCPChardev *s = (TCPChardev *)chr;
 
     int to_copy = (s->read_msgfds_num < num) ? s->read_msgfds_num : num;
 
@@ -2942,9 +2943,9 @@ static int tcp_get_msgfds(CharDriverState *chr, int *fds, int num)
     return to_copy;
 }
 
-static int tcp_set_msgfds(CharDriverState *chr, int *fds, int num)
+static int tcp_set_msgfds(Chardev *chr, int *fds, int num)
 {
-    TCPCharDriver *s = (TCPCharDriver *)chr;
+    TCPChardev *s = (TCPChardev *)chr;
 
     /* clear old pending fd array */
     g_free(s->write_msgfds);
@@ -2967,9 +2968,9 @@ static int tcp_set_msgfds(CharDriverState *chr, int *fds, int num)
     return 0;
 }
 
-static ssize_t tcp_chr_recv(CharDriverState *chr, char *buf, size_t len)
+static ssize_t tcp_chr_recv(Chardev *chr, char *buf, size_t len)
 {
-    TCPCharDriver *s = (TCPCharDriver *)chr;
+    TCPChardev *s = (TCPChardev *)chr;
     struct iovec iov = { .iov_base = buf, .iov_len = len };
     int ret;
     size_t i;
@@ -3024,15 +3025,15 @@ static ssize_t tcp_chr_recv(CharDriverState *chr, char *buf, size_t len)
     return ret;
 }
 
-static GSource *tcp_chr_add_watch(CharDriverState *chr, GIOCondition cond)
+static GSource *tcp_chr_add_watch(Chardev *chr, GIOCondition cond)
 {
-    TCPCharDriver *s = (TCPCharDriver *)chr;
+    TCPChardev *s = (TCPChardev *)chr;
     return qio_channel_create_watch(s->ioc, cond);
 }
 
-static void tcp_chr_free_connection(CharDriverState *chr)
+static void tcp_chr_free_connection(Chardev *chr)
 {
-    TCPCharDriver *s = (TCPCharDriver *)chr;
+    TCPChardev *s = (TCPChardev *)chr;
     int i;
 
     if (!s->connected) {
@@ -3059,9 +3060,9 @@ static void tcp_chr_free_connection(CharDriverState *chr)
     s->connected = 0;
 }
 
-static void tcp_chr_disconnect(CharDriverState *chr)
+static void tcp_chr_disconnect(Chardev *chr)
 {
-    TCPCharDriver *s = (TCPCharDriver *)chr;
+    TCPChardev *s = (TCPChardev *)chr;
 
     if (!s->connected) {
         return;
@@ -3083,8 +3084,8 @@ static void tcp_chr_disconnect(CharDriverState *chr)
 
 static gboolean tcp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
 {
-    CharDriverState *chr = opaque;
-    TCPCharDriver *s = opaque;
+    Chardev *chr = opaque;
+    TCPChardev *s = opaque;
     uint8_t buf[READ_BUF_LEN];
     int len, size;
 
@@ -3108,9 +3109,9 @@ static gboolean tcp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
     return TRUE;
 }
 
-static int tcp_chr_sync_read(CharDriverState *chr, const uint8_t *buf, int len)
+static int tcp_chr_sync_read(Chardev *chr, const uint8_t *buf, int len)
 {
-    TCPCharDriver *s = (TCPCharDriver *)chr;
+    TCPChardev *s = (TCPChardev *)chr;
     int size;
 
     if (!s->connected) {
@@ -3128,8 +3129,8 @@ static int tcp_chr_sync_read(CharDriverState *chr, const uint8_t *buf, int len)
 
 static void tcp_chr_connect(void *opaque)
 {
-    CharDriverState *chr = opaque;
-    TCPCharDriver *s = opaque;
+    Chardev *chr = opaque;
+    TCPChardev *s = opaque;
 
     g_free(chr->filename);
     chr->filename = sockaddr_to_str(
@@ -3147,10 +3148,10 @@ static void tcp_chr_connect(void *opaque)
     qemu_chr_be_generic_open(chr);
 }
 
-static void tcp_chr_update_read_handler(CharDriverState *chr,
+static void tcp_chr_update_read_handler(Chardev *chr,
                                         GMainContext *context)
 {
-    TCPCharDriver *s = (TCPCharDriver *)chr;
+    TCPChardev *s = (TCPChardev *)chr;
 
     if (!s->connected) {
         return;
@@ -3166,7 +3167,7 @@ static void tcp_chr_update_read_handler(CharDriverState *chr,
 }
 
 typedef struct {
-    CharDriverState *chr;
+    Chardev *chr;
     char buf[12];
     size_t buflen;
 } TCPCharDriverTelnetInit;
@@ -3199,9 +3200,9 @@ static gboolean tcp_chr_telnet_init_io(QIOChannel *ioc,
     return TRUE;
 }
 
-static void tcp_chr_telnet_init(CharDriverState *chr)
+static void tcp_chr_telnet_init(Chardev *chr)
 {
-    TCPCharDriver *s = (TCPCharDriver *)chr;
+    TCPChardev *s = (TCPChardev *)chr;
     TCPCharDriverTelnetInit *init =
         g_new0(TCPCharDriverTelnetInit, 1);
     size_t n = 0;
@@ -3235,8 +3236,8 @@ static void tcp_chr_telnet_init(CharDriverState *chr)
 static void tcp_chr_tls_handshake(QIOTask *task,
                                   gpointer user_data)
 {
-    CharDriverState *chr = user_data;
-    TCPCharDriver *s = user_data;
+    Chardev *chr = user_data;
+    TCPChardev *s = user_data;
 
     if (qio_task_propagate_error(task, NULL)) {
         tcp_chr_disconnect(chr);
@@ -3250,9 +3251,9 @@ static void tcp_chr_tls_handshake(QIOTask *task,
 }
 
 
-static void tcp_chr_tls_init(CharDriverState *chr)
+static void tcp_chr_tls_init(Chardev *chr)
 {
-    TCPCharDriver *s = (TCPCharDriver *)chr;
+    TCPChardev *s = (TCPChardev *)chr;
     QIOChannelTLS *tioc;
     Error *err = NULL;
     gchar *name;
@@ -3288,10 +3289,10 @@ static void tcp_chr_tls_init(CharDriverState *chr)
 }
 
 
-static void tcp_chr_set_client_ioc_name(CharDriverState *chr,
+static void tcp_chr_set_client_ioc_name(Chardev *chr,
                                         QIOChannelSocket *sioc)
 {
-    TCPCharDriver *s = (TCPCharDriver *)chr;
+    TCPChardev *s = (TCPChardev *)chr;
     char *name;
     name = g_strdup_printf("chardev-tcp-%s-%s",
                            s->is_listen ? "server" : "client",
@@ -3301,9 +3302,9 @@ static void tcp_chr_set_client_ioc_name(CharDriverState *chr,
 
 }
 
-static int tcp_chr_new_client(CharDriverState *chr, QIOChannelSocket *sioc)
+static int tcp_chr_new_client(Chardev *chr, QIOChannelSocket *sioc)
 {
-    TCPCharDriver *s = (TCPCharDriver *)chr;
+    TCPChardev *s = (TCPChardev *)chr;
 
     if (s->ioc != NULL) {
 	return -1;
@@ -3338,7 +3339,7 @@ static int tcp_chr_new_client(CharDriverState *chr, QIOChannelSocket *sioc)
 }
 
 
-static int tcp_chr_add_client(CharDriverState *chr, int fd)
+static int tcp_chr_add_client(Chardev *chr, int fd)
 {
     int ret;
     QIOChannelSocket *sioc;
@@ -3357,7 +3358,7 @@ static gboolean tcp_chr_accept(QIOChannel *channel,
                                GIOCondition cond,
                                void *opaque)
 {
-    CharDriverState *chr = opaque;
+    Chardev *chr = opaque;
     QIOChannelSocket *sioc;
 
     sioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(channel),
@@ -3373,9 +3374,9 @@ static gboolean tcp_chr_accept(QIOChannel *channel,
     return TRUE;
 }
 
-static int tcp_chr_wait_connected(CharDriverState *chr, Error **errp)
+static int tcp_chr_wait_connected(Chardev *chr, Error **errp)
 {
-    TCPCharDriver *s = (TCPCharDriver *)chr;
+    TCPChardev *s = (TCPChardev *)chr;
     QIOChannelSocket *sioc;
 
     /* It can't wait on s->connected, since it is set asynchronously
@@ -3402,7 +3403,7 @@ static int tcp_chr_wait_connected(CharDriverState *chr, Error **errp)
     return 0;
 }
 
-static int qemu_chr_wait_connected(CharDriverState *chr, Error **errp)
+static int qemu_chr_wait_connected(Chardev *chr, Error **errp)
 {
     if (chr->driver->chr_wait_connected) {
         return chr->driver->chr_wait_connected(chr, errp);
@@ -3421,9 +3422,9 @@ int qemu_chr_fe_wait_connected(CharBackend *be, Error **errp)
     return qemu_chr_wait_connected(be->chr, errp);
 }
 
-static void tcp_chr_free(CharDriverState *chr)
+static void tcp_chr_free(Chardev *chr)
 {
-    TCPCharDriver *s = (TCPCharDriver *)chr;
+    TCPChardev *s = (TCPChardev *)chr;
 
     tcp_chr_free_connection(chr);
 
@@ -3450,8 +3451,8 @@ static void tcp_chr_free(CharDriverState *chr)
 static void qemu_chr_socket_connected(QIOTask *task, void *opaque)
 {
     QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(qio_task_get_source(task));
-    CharDriverState *chr = opaque;
-    TCPCharDriver *s = (TCPCharDriver *)chr;
+    Chardev *chr = opaque;
+    TCPChardev *s = (TCPChardev *)chr;
     Error *err = NULL;
 
     if (qio_task_propagate_error(task, &err)) {
@@ -3472,24 +3473,24 @@ static void qemu_chr_socket_connected(QIOTask *task, void *opaque)
 /* Ring buffer chardev */
 
 typedef struct {
-    CharDriverState parent;
+    Chardev parent;
     size_t size;
     size_t prod;
     size_t cons;
     uint8_t *cbuf;
-} RingBufCharDriver;
+} RingBufChardev;
 
-static size_t ringbuf_count(const CharDriverState *chr)
+static size_t ringbuf_count(const Chardev *chr)
 {
-    const RingBufCharDriver *d = (RingBufCharDriver *)chr;
+    const RingBufChardev *d = (RingBufChardev *)chr;
 
     return d->prod - d->cons;
 }
 
 /* Called with chr_write_lock held.  */
-static int ringbuf_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+static int ringbuf_chr_write(Chardev *chr, const uint8_t *buf, int len)
 {
-    RingBufCharDriver *d = (RingBufCharDriver *)chr;
+    RingBufChardev *d = (RingBufChardev *)chr;
     int i;
 
     if (!buf || (len < 0)) {
@@ -3506,9 +3507,9 @@ static int ringbuf_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
     return len;
 }
 
-static int ringbuf_chr_read(CharDriverState *chr, uint8_t *buf, int len)
+static int ringbuf_chr_read(Chardev *chr, uint8_t *buf, int len)
 {
-    RingBufCharDriver *d = (RingBufCharDriver *)chr;
+    RingBufChardev *d = (RingBufChardev *)chr;
     int i;
 
     qemu_mutex_lock(&chr->chr_write_lock);
@@ -3520,30 +3521,30 @@ static int ringbuf_chr_read(CharDriverState *chr, uint8_t *buf, int len)
     return i;
 }
 
-static void ringbuf_chr_free(struct CharDriverState *chr)
+static void ringbuf_chr_free(struct Chardev *chr)
 {
-    RingBufCharDriver *d = (RingBufCharDriver *)chr;
+    RingBufChardev *d = (RingBufChardev *)chr;
 
     g_free(d->cbuf);
 }
 
-static CharDriverState *qemu_chr_open_ringbuf(const CharDriver *driver,
-                                              const char *id,
-                                              ChardevBackend *backend,
-                                              ChardevReturn *ret,
-                                              bool *be_opened,
-                                              Error **errp)
+static Chardev *qemu_chr_open_ringbuf(const CharDriver *driver,
+                                      const char *id,
+                                      ChardevBackend *backend,
+                                      ChardevReturn *ret,
+                                      bool *be_opened,
+                                      Error **errp)
 {
     ChardevRingbuf *opts = backend->u.ringbuf.data;
     ChardevCommon *common = qapi_ChardevRingbuf_base(opts);
-    CharDriverState *chr;
-    RingBufCharDriver *d;
+    Chardev *chr;
+    RingBufChardev *d;
 
     chr = qemu_chr_alloc(driver, common, errp);
     if (!chr) {
         return NULL;
     }
-    d = (RingBufCharDriver *)chr;
+    d = (RingBufChardev *)chr;
 
     d->size = opts->has_size ? opts->size : 65536;
 
@@ -3564,7 +3565,7 @@ fail:
     return NULL;
 }
 
-ChardevBackendKind qemu_chr_get_kind(const CharDriverState *chr)
+ChardevBackendKind qemu_chr_get_kind(const Chardev *chr)
 {
     return chr->driver->kind;
 }
@@ -3573,7 +3574,7 @@ void qmp_ringbuf_write(const char *device, const char *data,
                        bool has_format, enum DataFormat format,
                        Error **errp)
 {
-    CharDriverState *chr;
+    Chardev *chr;
     const uint8_t *write_data;
     int ret;
     gsize write_count;
@@ -3617,7 +3618,7 @@ char *qmp_ringbuf_read(const char *device, int64_t size,
                        bool has_format, enum DataFormat format,
                        Error **errp)
 {
-    CharDriverState *chr;
+    Chardev *chr;
     uint8_t *read_data;
     size_t count;
     char *data;
@@ -3850,12 +3851,12 @@ static const CharDriver stdio_driver = {
     .parse = qemu_chr_parse_stdio,
     .create = qemu_chr_open_stdio,
 #ifdef _WIN32
-    sizeof(WinStdioCharState),
+    sizeof(WinStdioChardev),
     .chr_write = win_stdio_write,
     .chr_set_echo = qemu_chr_set_echo_win_stdio,
     .chr_free = win_stdio_free,
 #else
-    sizeof(FDCharDriver),
+    sizeof(FDChardev),
     .chr_add_watch = fd_chr_add_watch,
     .chr_write = fd_chr_write,
     .chr_update_read_handler = fd_chr_update_read_handler,
@@ -3918,11 +3919,11 @@ static const CharDriver pipe_driver = {
     .parse = qemu_chr_parse_pipe,
     .create = qemu_chr_open_pipe,
 #ifdef _WIN32
-    sizeof(WinCharState),
+    sizeof(WinChardev),
     .chr_write = win_chr_write,
     .chr_free = win_chr_free,
 #else
-    sizeof(FDCharDriver),
+    sizeof(FDChardev),
     .chr_add_watch = fd_chr_add_watch,
     .chr_write = fd_chr_write,
     .chr_update_read_handler = fd_chr_update_read_handler,
@@ -3947,7 +3948,7 @@ static void qemu_chr_parse_ringbuf(QemuOpts *opts, ChardevBackend *backend,
 }
 
 static const CharDriver ringbuf_driver = {
-    .instance_size = sizeof(RingBufCharDriver),
+    .instance_size = sizeof(RingBufChardev),
     .kind = CHARDEV_BACKEND_KIND_RINGBUF,
     .parse = qemu_chr_parse_ringbuf,
     .create = qemu_chr_open_ringbuf,
@@ -3957,7 +3958,7 @@ static const CharDriver ringbuf_driver = {
 
 /* Bug-compatibility: */
 static const CharDriver memory_driver = {
-    .instance_size = sizeof(RingBufCharDriver),
+    .instance_size = sizeof(RingBufChardev),
     .kind = CHARDEV_BACKEND_KIND_MEMORY,
     .parse = qemu_chr_parse_ringbuf,
     .create = qemu_chr_open_ringbuf,
@@ -3981,7 +3982,7 @@ static void qemu_chr_parse_mux(QemuOpts *opts, ChardevBackend *backend,
 }
 
 static const CharDriver mux_driver = {
-    .instance_size = sizeof(MuxDriver),
+    .instance_size = sizeof(MuxChardev),
     .kind = CHARDEV_BACKEND_KIND_MUX,
     .parse = qemu_chr_parse_mux,
     .create = qemu_chr_open_mux,
@@ -4125,12 +4126,12 @@ void register_char_driver(const CharDriver *driver)
     backends[driver->kind] = driver;
 }
 
-CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
-                                        Error **errp)
+Chardev *qemu_chr_new_from_opts(QemuOpts *opts,
+                                Error **errp)
 {
     Error *local_err = NULL;
     const CharDriver *cd = NULL;
-    CharDriverState *chr;
+    Chardev *chr;
     int i;
     ChardevReturn *ret = NULL;
     ChardevBackend *backend;
@@ -4231,10 +4232,10 @@ err:
     return NULL;
 }
 
-CharDriverState *qemu_chr_new_noreplay(const char *label, const char *filename)
+Chardev *qemu_chr_new_noreplay(const char *label, const char *filename)
 {
     const char *p;
-    CharDriverState *chr;
+    Chardev *chr;
     QemuOpts *opts;
     Error *err = NULL;
 
@@ -4257,9 +4258,9 @@ CharDriverState *qemu_chr_new_noreplay(const char *label, const char *filename)
     return chr;
 }
 
-CharDriverState *qemu_chr_new(const char *label, const char *filename)
+Chardev *qemu_chr_new(const char *label, const char *filename)
 {
-    CharDriverState *chr;
+    Chardev *chr;
     chr = qemu_chr_new_noreplay(label, filename);
     if (chr) {
         if (replay_mode != REPLAY_MODE_NONE) {
@@ -4276,7 +4277,7 @@ CharDriverState *qemu_chr_new(const char *label, const char *filename)
 
 void qemu_chr_fe_set_echo(CharBackend *be, bool echo)
 {
-    CharDriverState *chr = be->chr;
+    Chardev *chr = be->chr;
 
     if (chr && chr->driver->chr_set_echo) {
         chr->driver->chr_set_echo(chr, echo);
@@ -4285,7 +4286,7 @@ void qemu_chr_fe_set_echo(CharBackend *be, bool echo)
 
 void qemu_chr_fe_set_open(CharBackend *be, int fe_open)
 {
-    CharDriverState *chr = be->chr;
+    Chardev *chr = be->chr;
 
     if (!chr) {
         return;
@@ -4303,7 +4304,7 @@ void qemu_chr_fe_set_open(CharBackend *be, int fe_open)
 guint qemu_chr_fe_add_watch(CharBackend *be, GIOCondition cond,
                             GIOFunc func, void *user_data)
 {
-    CharDriverState *s = be->chr;
+    Chardev *s = be->chr;
     GSource *src;
     guint tag;
 
@@ -4325,14 +4326,14 @@ guint qemu_chr_fe_add_watch(CharBackend *be, GIOCondition cond,
 
 void qemu_chr_fe_disconnect(CharBackend *be)
 {
-    CharDriverState *chr = be->chr;
+    Chardev *chr = be->chr;
 
     if (chr && chr->driver->chr_disconnect) {
         chr->driver->chr_disconnect(chr);
     }
 }
 
-static void qemu_chr_free_common(CharDriverState *chr)
+static void qemu_chr_free_common(Chardev *chr)
 {
     if (chr->be) {
         chr->be->chr = NULL;
@@ -4346,7 +4347,7 @@ static void qemu_chr_free_common(CharDriverState *chr)
     g_free(chr);
 }
 
-void qemu_chr_free(CharDriverState *chr)
+void qemu_chr_free(Chardev *chr)
 {
     if (chr->driver->chr_free) {
         chr->driver->chr_free(chr);
@@ -4354,7 +4355,7 @@ void qemu_chr_free(CharDriverState *chr)
     qemu_chr_free_common(chr);
 }
 
-void qemu_chr_delete(CharDriverState *chr)
+void qemu_chr_delete(Chardev *chr)
 {
     QTAILQ_REMOVE(&chardevs, chr, next);
     qemu_chr_free(chr);
@@ -4363,7 +4364,7 @@ void qemu_chr_delete(CharDriverState *chr)
 ChardevInfoList *qmp_query_chardev(Error **errp)
 {
     ChardevInfoList *chr_list = NULL;
-    CharDriverState *chr;
+    Chardev *chr;
 
     QTAILQ_FOREACH(chr, &chardevs, next) {
         ChardevInfoList *info = g_malloc0(sizeof(*info));
@@ -4411,9 +4412,9 @@ ChardevBackendInfoList *qmp_query_chardev_backends(Error **errp)
     return backend_list;
 }
 
-CharDriverState *qemu_chr_find(const char *name)
+Chardev *qemu_chr_find(const char *name)
 {
-    CharDriverState *chr;
+    Chardev *chr;
 
     QTAILQ_FOREACH(chr, &chardevs, next) {
         if (strcmp(chr->label, name) != 0)
@@ -4519,12 +4520,12 @@ QemuOptsList qemu_chardev_opts = {
 
 #ifdef _WIN32
 
-static CharDriverState *qmp_chardev_open_file(const CharDriver *driver,
-                                              const char *id,
-                                              ChardevBackend *backend,
-                                              ChardevReturn *ret,
-                                              bool *be_opened,
-                                              Error **errp)
+static Chardev *qmp_chardev_open_file(const CharDriver *driver,
+                                      const char *id,
+                                      ChardevBackend *backend,
+                                      ChardevReturn *ret,
+                                      bool *be_opened,
+                                      Error **errp)
 {
     ChardevFile *file = backend->u.file.data;
     ChardevCommon *common = qapi_ChardevFile_base(file);
@@ -4556,16 +4557,16 @@ static CharDriverState *qmp_chardev_open_file(const CharDriver *driver,
     return qemu_chr_open_win_file(driver, out, common, errp);
 }
 
-static CharDriverState *qmp_chardev_open_serial(const CharDriver *driver,
-                                                const char *id,
-                                                ChardevBackend *backend,
-                                                ChardevReturn *ret,
-                                                bool *be_opened,
-                                                Error **errp)
+static Chardev *qmp_chardev_open_serial(const CharDriver *driver,
+                                        const char *id,
+                                        ChardevBackend *backend,
+                                        ChardevReturn *ret,
+                                        bool *be_opened,
+                                        Error **errp)
 {
     ChardevHostdev *serial = backend->u.serial.data;
     ChardevCommon *common = qapi_ChardevHostdev_base(serial);
-    CharDriverState *chr;
+    Chardev *chr;
 
     chr = qemu_chr_alloc(driver, common, errp);
     if (!chr) {
@@ -4594,12 +4595,12 @@ static int qmp_chardev_open_file_source(char *src, int flags,
     return fd;
 }
 
-static CharDriverState *qmp_chardev_open_file(const CharDriver *driver,
-                                              const char *id,
-                                              ChardevBackend *backend,
-                                              ChardevReturn *ret,
-                                              bool *be_opened,
-                                              Error **errp)
+static Chardev *qmp_chardev_open_file(const CharDriver *driver,
+                                      const char *id,
+                                      ChardevBackend *backend,
+                                      ChardevReturn *ret,
+                                      bool *be_opened,
+                                      Error **errp)
 {
     ChardevFile *file = backend->u.file.data;
     ChardevCommon *common = qapi_ChardevFile_base(file);
@@ -4630,12 +4631,12 @@ static CharDriverState *qmp_chardev_open_file(const CharDriver *driver,
 }
 
 #ifdef HAVE_CHARDEV_SERIAL
-static CharDriverState *qmp_chardev_open_serial(const CharDriver *driver,
-                                                const char *id,
-                                                ChardevBackend *backend,
-                                                ChardevReturn *ret,
-                                                bool *be_opened,
-                                                Error **errp)
+static Chardev *qmp_chardev_open_serial(const CharDriver *driver,
+                                        const char *id,
+                                        ChardevBackend *backend,
+                                        ChardevReturn *ret,
+                                        bool *be_opened,
+                                        Error **errp)
 {
     ChardevHostdev *serial = backend->u.serial.data;
     ChardevCommon *common = qapi_ChardevHostdev_base(serial);
@@ -4653,12 +4654,12 @@ static CharDriverState *qmp_chardev_open_serial(const CharDriver *driver,
 #endif
 
 #ifdef HAVE_CHARDEV_PARPORT
-static CharDriverState *qmp_chardev_open_parallel(const CharDriver *driver,
-                                                  const char *id,
-                                                  ChardevBackend *backend,
-                                                  ChardevReturn *ret,
-                                                  bool *be_opened,
-                                                  Error **errp)
+static Chardev *qmp_chardev_open_parallel(const CharDriver *driver,
+                                          const char *id,
+                                          ChardevBackend *backend,
+                                          ChardevReturn *ret,
+                                          bool *be_opened,
+                                          Error **errp)
 {
     ChardevHostdev *parallel = backend->u.parallel.data;
     ChardevCommon *common = qapi_ChardevHostdev_base(parallel);
@@ -4672,7 +4673,7 @@ static CharDriverState *qmp_chardev_open_parallel(const CharDriver *driver,
 }
 
 static const CharDriver parallel_driver = {
-    .instance_size = sizeof(ParallelCharDriver),
+    .instance_size = sizeof(ParallelChardev),
     .kind = CHARDEV_BACKEND_KIND_PARALLEL,
     .alias = "parport",
     .parse = qemu_chr_parse_parallel,
@@ -4696,11 +4697,11 @@ static const CharDriver file_driver = {
     .parse = qemu_chr_parse_file_out,
     .create = qmp_chardev_open_file,
 #ifdef _WIN32
-    sizeof(WinCharState),
+    sizeof(WinChardev),
     .chr_write = win_chr_write,
     /* FIXME: no chr_free */
 #else
-    sizeof(FDCharDriver),
+    sizeof(FDChardev),
     .chr_add_watch = fd_chr_add_watch,
     .chr_write = fd_chr_write,
     .chr_update_read_handler = fd_chr_update_read_handler,
@@ -4715,11 +4716,11 @@ static const CharDriver serial_driver = {
     .parse = qemu_chr_parse_serial,
     .create = qmp_chardev_open_serial,
 #ifdef _WIN32
-    sizeof(WinCharState),
+    sizeof(WinChardev),
     .chr_write = win_chr_write,
     .chr_free = win_chr_free,
 #else
-    sizeof(FDCharDriver),
+    sizeof(FDChardev),
     .chr_add_watch = fd_chr_add_watch,
     .chr_write = fd_chr_write,
     .chr_update_read_handler = fd_chr_update_read_handler,
@@ -4731,8 +4732,8 @@ static const CharDriver serial_driver = {
 
 static gboolean socket_reconnect_timeout(gpointer opaque)
 {
-    CharDriverState *chr = opaque;
-    TCPCharDriver *s = opaque;
+    Chardev *chr = opaque;
+    TCPChardev *s = opaque;
     QIOChannelSocket *sioc;
 
     s->reconnect_timer = 0;
@@ -4750,15 +4751,15 @@ static gboolean socket_reconnect_timeout(gpointer opaque)
     return false;
 }
 
-static CharDriverState *qmp_chardev_open_socket(const CharDriver *driver,
-                                                const char *id,
-                                                ChardevBackend *backend,
-                                                ChardevReturn *ret,
-                                                bool *be_opened,
-                                                Error **errp)
+static Chardev *qmp_chardev_open_socket(const CharDriver *driver,
+                                        const char *id,
+                                        ChardevBackend *backend,
+                                        ChardevReturn *ret,
+                                        bool *be_opened,
+                                        Error **errp)
 {
-    CharDriverState *chr;
-    TCPCharDriver *s;
+    Chardev *chr;
+    TCPChardev *s;
     ChardevSocket *sock = backend->u.socket.data;
     SocketAddress *addr = sock->addr;
     bool do_nodelay     = sock->has_nodelay ? sock->nodelay : false;
@@ -4773,7 +4774,7 @@ static CharDriverState *qmp_chardev_open_socket(const CharDriver *driver,
     if (!chr) {
         return NULL;
     }
-    s = (TCPCharDriver *)chr;
+    s = (TCPChardev *)chr;
 
     s->is_unix = addr->type == SOCKET_ADDRESS_KIND_UNIX;
     s->is_listen = is_listen;
@@ -4880,7 +4881,7 @@ static CharDriverState *qmp_chardev_open_socket(const CharDriver *driver,
 }
 
 static const CharDriver socket_driver = {
-    .instance_size = sizeof(TCPCharDriver),
+    .instance_size = sizeof(TCPChardev),
     .kind = CHARDEV_BACKEND_KIND_SOCKET,
     .parse = qemu_chr_parse_socket,
     .create = qmp_chardev_open_socket,
@@ -4896,19 +4897,19 @@ static const CharDriver socket_driver = {
     .chr_free = tcp_chr_free,
 };
 
-static CharDriverState *qmp_chardev_open_udp(const CharDriver *driver,
-                                             const char *id,
-                                             ChardevBackend *backend,
-                                             ChardevReturn *ret,
-                                             bool *be_opened,
-                                             Error **errp)
+static Chardev *qmp_chardev_open_udp(const CharDriver *driver,
+                                     const char *id,
+                                     ChardevBackend *backend,
+                                     ChardevReturn *ret,
+                                     bool *be_opened,
+                                     Error **errp)
 {
     ChardevUdp *udp = backend->u.udp.data;
     ChardevCommon *common = qapi_ChardevUdp_base(udp);
     QIOChannelSocket *sioc = qio_channel_socket_new();
     char *name;
-    CharDriverState *chr;
-    NetCharDriver *s;
+    Chardev *chr;
+    NetChardev *s;
 
     if (qio_channel_socket_dgram_sync(sioc,
                                       udp->local, udp->remote,
@@ -4926,7 +4927,7 @@ static CharDriverState *qmp_chardev_open_udp(const CharDriver *driver,
     qio_channel_set_name(QIO_CHANNEL(sioc), name);
     g_free(name);
 
-    s = (NetCharDriver *)chr;
+    s = (NetChardev *)chr;
     s->ioc = QIO_CHANNEL(sioc);
     /* be isn't opened until we get a connection */
     *be_opened = false;
@@ -4935,7 +4936,7 @@ static CharDriverState *qmp_chardev_open_udp(const CharDriver *driver,
 }
 
 static const CharDriver udp_driver = {
-    .instance_size = sizeof(NetCharDriver),
+    .instance_size = sizeof(NetChardev),
     .kind = CHARDEV_BACKEND_KIND_UDP,
     .parse = qemu_chr_parse_udp,
     .create = qmp_chardev_open_udp,
@@ -4944,13 +4945,13 @@ static const CharDriver udp_driver = {
     .chr_free = udp_chr_free,
 };
 
-bool qemu_chr_has_feature(CharDriverState *chr,
+bool qemu_chr_has_feature(Chardev *chr,
                           CharDriverFeature feature)
 {
     return test_bit(feature, chr->features);
 }
 
-void qemu_chr_set_feature(CharDriverState *chr,
+void qemu_chr_set_feature(Chardev *chr,
                            CharDriverFeature feature)
 {
     return set_bit(feature, chr->features);
@@ -4960,7 +4961,7 @@ ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
                                Error **errp)
 {
     ChardevReturn *ret = g_new0(ChardevReturn, 1);
-    CharDriverState *chr = NULL;
+    Chardev *chr = NULL;
     const CharDriver *cd;
     Error *local_err = NULL;
     bool be_opened = true;
@@ -5001,7 +5002,7 @@ out_error:
 
 void qmp_chardev_remove(const char *id, Error **errp)
 {
-    CharDriverState *chr;
+    Chardev *chr;
 
     chr = qemu_chr_find(id);
     if (chr == NULL) {
@@ -5022,7 +5023,7 @@ void qmp_chardev_remove(const char *id, Error **errp)
 
 void qemu_chr_cleanup(void)
 {
-    CharDriverState *chr, *tmp;
+    Chardev *chr, *tmp;
 
     QTAILQ_FOREACH_SAFE(chr, &chardevs, next, tmp) {
         qemu_chr_delete(chr);
diff --git a/qmp.c b/qmp.c
index 0028f0b..d955406 100644
--- a/qmp.c
+++ b/qmp.c
@@ -616,7 +616,7 @@ void qmp_add_client(const char *protocol, const char *fdname,
                     bool has_skipauth, bool skipauth, bool has_tls, bool tls,
                     Error **errp)
 {
-    CharDriverState *s;
+    Chardev *s;
     int fd;
 
     fd = monitor_get_fd(cur_mon, fdname, errp);
diff --git a/qtest.c b/qtest.c
index bd9d417..1446719 100644
--- a/qtest.c
+++ b/qtest.c
@@ -670,7 +670,7 @@ static int qtest_init_accel(MachineState *ms)
 
 void qtest_init(const char *qtest_chrdev, const char *qtest_log, Error **errp)
 {
-    CharDriverState *chr;
+    Chardev *chr;
 
     chr = qemu_chr_new("qtest", qtest_chrdev);
 
diff --git a/replay/replay-char.c b/replay/replay-char.c
index edf46ab..aa65955 100755
--- a/replay/replay-char.c
+++ b/replay/replay-char.c
@@ -18,7 +18,7 @@
 
 /* Char drivers that generate qemu_chr_be_write events
    that should be saved into the log. */
-static CharDriverState **char_drivers;
+static Chardev **char_drivers;
 static int drivers_count;
 
 /* Char event attributes. */
@@ -28,7 +28,7 @@ typedef struct CharEvent {
     size_t len;
 } CharEvent;
 
-static int find_char_driver(CharDriverState *chr)
+static int find_char_driver(Chardev *chr)
 {
     int i = 0;
     for ( ; i < drivers_count ; ++i) {
@@ -39,7 +39,7 @@ static int find_char_driver(CharDriverState *chr)
     return -1;
 }
 
-void replay_register_char_driver(CharDriverState *chr)
+void replay_register_char_driver(Chardev *chr)
 {
     if (replay_mode == REPLAY_MODE_NONE) {
         return;
@@ -49,7 +49,7 @@ void replay_register_char_driver(CharDriverState *chr)
     char_drivers[drivers_count++] = chr;
 }
 
-void replay_chr_be_write(CharDriverState *s, uint8_t *buf, int len)
+void replay_chr_be_write(Chardev *s, uint8_t *buf, int len)
 {
     CharEvent *event = g_malloc0(sizeof(CharEvent));
 
diff --git a/spice-qemu-char.c b/spice-qemu-char.c
index 1aed451..38b1f6c 100644
--- a/spice-qemu-char.c
+++ b/spice-qemu-char.c
@@ -6,29 +6,29 @@
 #include <spice/protocol.h>
 
 
-typedef struct SpiceCharDriver {
-    CharDriverState       parent;
+typedef struct SpiceChardev {
+    Chardev               parent;
 
-    SpiceCharDeviceInstance     sin;
+    SpiceCharDeviceInstance sin;
     bool                  active;
     bool                  blocked;
     const uint8_t         *datapos;
     int                   datalen;
-    QLIST_ENTRY(SpiceCharDriver) next;
-} SpiceCharDriver;
+    QLIST_ENTRY(SpiceChardev) next;
+} SpiceChardev;
 
 typedef struct SpiceCharSource {
     GSource               source;
-    SpiceCharDriver       *scd;
+    SpiceChardev       *scd;
 } SpiceCharSource;
 
-static QLIST_HEAD(, SpiceCharDriver) spice_chars =
+static QLIST_HEAD(, SpiceChardev) spice_chars =
     QLIST_HEAD_INITIALIZER(spice_chars);
 
 static int vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len)
 {
-    SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin);
-    CharDriverState *chr = (CharDriverState *)scd;
+    SpiceChardev *scd = container_of(sin, SpiceChardev, sin);
+    Chardev *chr = (Chardev *)scd;
     ssize_t out = 0;
     ssize_t last_out;
     uint8_t* p = (uint8_t*)buf;
@@ -51,7 +51,7 @@ static int vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len)
 
 static int vmc_read(SpiceCharDeviceInstance *sin, uint8_t *buf, int len)
 {
-    SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin);
+    SpiceChardev *scd = container_of(sin, SpiceChardev, sin);
     int bytes = MIN(len, scd->datalen);
 
     if (bytes > 0) {
@@ -71,8 +71,8 @@ static int vmc_read(SpiceCharDeviceInstance *sin, uint8_t *buf, int len)
 #if SPICE_SERVER_VERSION >= 0x000c02
 static void vmc_event(SpiceCharDeviceInstance *sin, uint8_t event)
 {
-    SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin);
-    CharDriverState *chr = (CharDriverState *)scd;
+    SpiceChardev *scd = container_of(sin, SpiceChardev, sin);
+    Chardev *chr = (Chardev *)scd;
     int chr_event;
 
     switch (event) {
@@ -90,8 +90,8 @@ static void vmc_event(SpiceCharDeviceInstance *sin, uint8_t event)
 
 static void vmc_state(SpiceCharDeviceInstance *sin, int connected)
 {
-    SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin);
-    CharDriverState *chr = (CharDriverState *)scd;
+    SpiceChardev *scd = container_of(sin, SpiceChardev, sin);
+    Chardev *chr = (Chardev *)scd;
 
     if ((chr->be_open && connected) ||
         (!chr->be_open && !connected)) {
@@ -119,7 +119,7 @@ static SpiceCharDeviceInterface vmc_interface = {
 };
 
 
-static void vmc_register_interface(SpiceCharDriver *scd)
+static void vmc_register_interface(SpiceChardev *scd)
 {
     if (scd->active) {
         return;
@@ -130,7 +130,7 @@ static void vmc_register_interface(SpiceCharDriver *scd)
     trace_spice_vmc_register_interface(scd);
 }
 
-static void vmc_unregister_interface(SpiceCharDriver *scd)
+static void vmc_unregister_interface(SpiceChardev *scd)
 {
     if (!scd->active) {
         return;
@@ -170,9 +170,9 @@ static GSourceFuncs SpiceCharSourceFuncs = {
     .dispatch = spice_char_source_dispatch,
 };
 
-static GSource *spice_chr_add_watch(CharDriverState *chr, GIOCondition cond)
+static GSource *spice_chr_add_watch(Chardev *chr, GIOCondition cond)
 {
-    SpiceCharDriver *scd = (SpiceCharDriver *)chr;
+    SpiceChardev *scd = (SpiceChardev *)chr;
     SpiceCharSource *src;
 
     assert(cond & G_IO_OUT);
@@ -184,9 +184,9 @@ static GSource *spice_chr_add_watch(CharDriverState *chr, GIOCondition cond)
     return (GSource *)src;
 }
 
-static int spice_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+static int spice_chr_write(Chardev *chr, const uint8_t *buf, int len)
 {
-    SpiceCharDriver *s = (SpiceCharDriver *)chr;
+    SpiceChardev *s = (SpiceChardev *)chr;
     int read_bytes;
 
     assert(s->datalen == 0);
@@ -203,9 +203,9 @@ static int spice_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
     return read_bytes;
 }
 
-static void spice_chr_free(struct CharDriverState *chr)
+static void spice_chr_free(struct Chardev *chr)
 {
-    SpiceCharDriver *s = (SpiceCharDriver *)chr;
+    SpiceChardev *s = (SpiceChardev *)chr;
 
     vmc_unregister_interface(s);
     QLIST_REMOVE(s, next);
@@ -216,9 +216,9 @@ static void spice_chr_free(struct CharDriverState *chr)
 #endif
 }
 
-static void spice_vmc_set_fe_open(struct CharDriverState *chr, int fe_open)
+static void spice_vmc_set_fe_open(struct Chardev *chr, int fe_open)
 {
-    SpiceCharDriver *s = (SpiceCharDriver *)chr;
+    SpiceChardev *s = (SpiceChardev *)chr;
     if (fe_open) {
         vmc_register_interface(s);
     } else {
@@ -226,10 +226,10 @@ static void spice_vmc_set_fe_open(struct CharDriverState *chr, int fe_open)
     }
 }
 
-static void spice_port_set_fe_open(struct CharDriverState *chr, int fe_open)
+static void spice_port_set_fe_open(struct Chardev *chr, int fe_open)
 {
 #if SPICE_SERVER_VERSION >= 0x000c02
-    SpiceCharDriver *s = (SpiceCharDriver *)chr;
+    SpiceChardev *s = (SpiceChardev *)chr;
 
     if (fe_open) {
         spice_server_port_event(&s->sin, SPICE_PORT_EVENT_OPENED);
@@ -256,26 +256,26 @@ static void print_allowed_subtypes(void)
     fprintf(stderr, "\n");
 }
 
-static void spice_chr_accept_input(struct CharDriverState *chr)
+static void spice_chr_accept_input(struct Chardev *chr)
 {
-    SpiceCharDriver *s = (SpiceCharDriver *)chr;
+    SpiceChardev *s = (SpiceChardev *)chr;
 
     spice_server_char_device_wakeup(&s->sin);
 }
 
-static CharDriverState *chr_open(const CharDriver *driver,
+static Chardev *chr_open(const CharDriver *driver,
                                  const char *subtype,
                                  ChardevCommon *backend,
                                  Error **errp)
 {
-    CharDriverState *chr;
-    SpiceCharDriver *s;
+    Chardev *chr;
+    SpiceChardev *s;
 
     chr = qemu_chr_alloc(driver, backend, errp);
     if (!chr) {
         return NULL;
     }
-    s = (SpiceCharDriver *)chr;
+    s = (SpiceChardev *)chr;
     s->active = false;
     s->sin.subtype = g_strdup(subtype);
 
@@ -284,7 +284,7 @@ static CharDriverState *chr_open(const CharDriver *driver,
     return chr;
 }
 
-static CharDriverState *qemu_chr_open_spice_vmc(const CharDriver *driver,
+static Chardev *qemu_chr_open_spice_vmc(const CharDriver *driver,
                                                 const char *id,
                                                 ChardevBackend *backend,
                                                 ChardevReturn *ret,
@@ -312,7 +312,7 @@ static CharDriverState *qemu_chr_open_spice_vmc(const CharDriver *driver,
 }
 
 #if SPICE_SERVER_VERSION >= 0x000c02
-static CharDriverState *qemu_chr_open_spice_port(const CharDriver *driver,
+static Chardev *qemu_chr_open_spice_port(const CharDriver *driver,
                                                  const char *id,
                                                  ChardevBackend *backend,
                                                  ChardevReturn *ret,
@@ -322,8 +322,8 @@ static CharDriverState *qemu_chr_open_spice_port(const CharDriver *driver,
     ChardevSpicePort *spiceport = backend->u.spiceport.data;
     const char *name = spiceport->fqdn;
     ChardevCommon *common = qapi_ChardevSpicePort_base(spiceport);
-    CharDriverState *chr;
-    SpiceCharDriver *s;
+    Chardev *chr;
+    SpiceChardev *s;
 
     if (name == NULL) {
         fprintf(stderr, "spice-qemu-char: missing name parameter\n");
@@ -335,7 +335,7 @@ static CharDriverState *qemu_chr_open_spice_port(const CharDriver *driver,
         return NULL;
     }
     *be_opened = false;
-    s = (SpiceCharDriver *)chr;
+    s = (SpiceChardev *)chr;
     s->sin.portname = g_strdup(name);
 
     return chr;
@@ -343,7 +343,7 @@ static CharDriverState *qemu_chr_open_spice_port(const CharDriver *driver,
 
 void qemu_spice_register_ports(void)
 {
-    SpiceCharDriver *s;
+    SpiceChardev *s;
 
     QLIST_FOREACH(s, &spice_chars, next) {
         if (s->sin.portname == NULL) {
@@ -387,7 +387,7 @@ static void qemu_chr_parse_spice_port(QemuOpts *opts, ChardevBackend *backend,
 static void register_types(void)
 {
     static const CharDriver vmc_driver = {
-        .instance_size = sizeof(SpiceCharDriver),
+        .instance_size = sizeof(SpiceChardev),
         .kind = CHARDEV_BACKEND_KIND_SPICEVMC,
         .parse = qemu_chr_parse_spice_vmc,
         .create = qemu_chr_open_spice_vmc,
@@ -398,7 +398,7 @@ static void register_types(void)
         .chr_free = spice_chr_free,
     };
     static const CharDriver port_driver = {
-        .instance_size = sizeof(SpiceCharDriver),
+        .instance_size = sizeof(SpiceChardev),
         .kind = CHARDEV_BACKEND_KIND_SPICEPORT,
         .parse = qemu_chr_parse_spice_port,
         .create = qemu_chr_open_spice_port,
diff --git a/stubs/monitor.c b/stubs/monitor.c
index 1d574b1..e018c8f 100644
--- a/stubs/monitor.c
+++ b/stubs/monitor.c
@@ -11,6 +11,6 @@ int monitor_get_fd(Monitor *mon, const char *name, Error **errp)
     return -1;
 }
 
-void monitor_init(CharDriverState *chr, int flags)
+void monitor_init(Chardev *chr, int flags)
 {
 }
diff --git a/stubs/replay.c b/stubs/replay.c
index d9a6da9..9c8aa48 100644
--- a/stubs/replay.c
+++ b/stubs/replay.c
@@ -30,11 +30,11 @@ void replay_finish(void)
 {
 }
 
-void replay_register_char_driver(CharDriverState *chr)
+void replay_register_char_driver(Chardev *chr)
 {
 }
 
-void replay_chr_be_write(CharDriverState *s, uint8_t *buf, int len)
+void replay_chr_be_write(Chardev *s, uint8_t *buf, int len)
 {
     abort();
 }
diff --git a/tests/test-char.c b/tests/test-char.c
index 241685a..da69f11 100644
--- a/tests/test-char.c
+++ b/tests/test-char.c
@@ -40,7 +40,7 @@ static void fe_event(void *opaque, int event)
 #ifdef CONFIG_HAS_GLIB_SUBPROCESS_TESTS
 static void char_stdio_test_subprocess(void)
 {
-    CharDriverState *chr;
+    Chardev *chr;
     CharBackend be;
     int ret;
 
@@ -68,7 +68,7 @@ static void char_stdio_test(void)
 static void char_ringbuf_test(void)
 {
     QemuOpts *opts;
-    CharDriverState *chr;
+    Chardev *chr;
     CharBackend be;
     char *data;
     int ret;
@@ -109,7 +109,7 @@ static void char_ringbuf_test(void)
 static void char_mux_test(void)
 {
     QemuOpts *opts;
-    CharDriverState *chr, *base;
+    Chardev *chr, *base;
     char *data;
     FeHandler h1 = { 0, }, h2 = { 0, };
     CharBackend chr_be1, chr_be2;
@@ -185,7 +185,7 @@ static void char_mux_test(void)
 static void char_null_test(void)
 {
     Error *err = NULL;
-    CharDriverState *chr;
+    Chardev *chr;
     CharBackend be;
     int ret;
 
@@ -227,7 +227,7 @@ static void char_null_test(void)
 
 static void char_invalid_test(void)
 {
-    CharDriverState *chr;
+    Chardev *chr;
 
     chr = qemu_chr_new("label-invalid", "invalid");
     g_assert_null(chr);
diff --git a/tests/vhost-user-test.c b/tests/vhost-user-test.c
index 96bf00e..f3ac6ea 100644
--- a/tests/vhost-user-test.c
+++ b/tests/vhost-user-test.c
@@ -454,7 +454,7 @@ static void chr_event(void *opaque, int event)
 static void test_server_create_chr(TestServer *server, const gchar *opt)
 {
     gchar *chr_path;
-    CharDriverState *chr;
+    Chardev *chr;
 
     chr_path = g_strdup_printf("unix:%s%s", server->socket_path, opt);
     chr = qemu_chr_new(server->chr_name, chr_path);
@@ -486,7 +486,7 @@ static inline void test_server_connect(TestServer *server)
 static gboolean _test_server_free(TestServer *server)
 {
     int i;
-    CharDriverState *chr = qemu_chr_fe_get_driver(&server->chr);
+    Chardev *chr = qemu_chr_fe_get_driver(&server->chr);
 
     qemu_chr_fe_deinit(&server->chr);
     qemu_chr_delete(chr);
diff --git a/ui/console.c b/ui/console.c
index 495d5a2..7420f28 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -158,7 +158,7 @@ struct QemuConsole {
     int esc_params[MAX_ESC_PARAMS];
     int nb_esc_params;
 
-    CharDriverState *chr;
+    Chardev *chr;
     /* fifo for key pressed */
     QEMUFIFO out_fifo;
     uint8_t out_fifo_buf[16];
@@ -183,7 +183,7 @@ static int nb_consoles = 0;
 static bool cursor_visible_phase;
 static QEMUTimer *cursor_timer;
 
-static void text_console_do_init(CharDriverState *chr, DisplayState *ds);
+static void text_console_do_init(Chardev *chr, DisplayState *ds);
 static void dpy_refresh(DisplayState *s);
 static DisplayState *get_alloc_displaystate(void);
 static void text_console_update_cursor_timer(void);
@@ -1046,14 +1046,14 @@ void console_select(unsigned int index)
     }
 }
 
-typedef struct VCDriverState {
-    CharDriverState parent;
+typedef struct VCChardev {
+    Chardev parent;
     QemuConsole *console;
-} VCDriverState;
+} VCChardev;
 
-static int console_puts(CharDriverState *chr, const uint8_t *buf, int len)
+static int console_puts(Chardev *chr, const uint8_t *buf, int len)
 {
-    VCDriverState *drv = (VCDriverState *)chr;
+    VCChardev *drv = (VCChardev *)chr;
     QemuConsole *s = drv->console;
     int i;
 
@@ -1962,9 +1962,9 @@ int qemu_console_get_height(QemuConsole *con, int fallback)
     return con ? surface_height(con->surface) : fallback;
 }
 
-static void text_console_set_echo(CharDriverState *chr, bool echo)
+static void text_console_set_echo(Chardev *chr, bool echo)
 {
-    VCDriverState *drv = (VCDriverState *)chr;
+    VCChardev *drv = (VCChardev *)chr;
     QemuConsole *s = drv->console;
 
     s->echo = echo;
@@ -2003,9 +2003,9 @@ static const GraphicHwOps text_console_ops = {
     .text_update = text_console_update,
 };
 
-static void text_console_do_init(CharDriverState *chr, DisplayState *ds)
+static void text_console_do_init(Chardev *chr, DisplayState *ds)
 {
-    VCDriverState *drv = (VCDriverState *)chr;
+    VCChardev *drv = (VCChardev *)chr;
     QemuConsole *s = drv->console;
     int g_width = 80 * FONT_WIDTH;
     int g_height = 24 * FONT_HEIGHT;
@@ -2058,11 +2058,11 @@ static void text_console_do_init(CharDriverState *chr, DisplayState *ds)
 
 static const CharDriver vc_driver;
 
-static CharDriverState *text_console_init(ChardevVC *vc, Error **errp)
+static Chardev *text_console_init(ChardevVC *vc, Error **errp)
 {
     ChardevCommon *common = qapi_ChardevVC_base(vc);
-    CharDriverState *chr;
-    VCDriverState *drv;
+    Chardev *chr;
+    VCChardev *drv;
     QemuConsole *s;
     unsigned width = 0;
     unsigned height = 0;
@@ -2099,7 +2099,7 @@ static CharDriverState *text_console_init(ChardevVC *vc, Error **errp)
     }
 
     s->chr = chr;
-    drv = (VCDriverState *)chr;
+    drv = (VCChardev *)chr;
     drv->console = s;
 
     if (display_state) {
@@ -2110,10 +2110,10 @@ static CharDriverState *text_console_init(ChardevVC *vc, Error **errp)
 
 static VcHandler *vc_handler = text_console_init;
 
-static CharDriverState *vc_init(const CharDriver *driver,
-                                const char *id, ChardevBackend *backend,
-                                ChardevReturn *ret, bool *be_opened,
-                                Error **errp)
+static Chardev *vc_init(const CharDriver *driver,
+                        const char *id, ChardevBackend *backend,
+                        ChardevReturn *ret, bool *be_opened,
+                        Error **errp)
 {
     /* console/chardev init sometimes completes elsewhere in a 2nd
      * stage, so defer OPENED events until they are fully initialized
@@ -2204,7 +2204,7 @@ static const TypeInfo qemu_console_info = {
 };
 
 static const CharDriver vc_driver = {
-    .instance_size = sizeof(VCDriverState),
+    .instance_size = sizeof(VCChardev),
     .kind = CHARDEV_BACKEND_KIND_VC,
     .parse = qemu_chr_parse_vc,
     .create = vc_init,
diff --git a/ui/gtk.c b/ui/gtk.c
index 90cf4d5..175871a 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -181,11 +181,11 @@ struct GtkDisplayState {
     bool ignore_keys;
 };
 
-typedef struct VCDriverState {
-    CharDriverState parent;
+typedef struct VCChardev {
+    Chardev parent;
     VirtualConsole *console;
     bool echo;
-} VCDriverState;
+} VCChardev;
 
 static void gd_grab_pointer(VirtualConsole *vc, const char *reason);
 static void gd_ungrab_pointer(GtkDisplayState *s);
@@ -1689,18 +1689,18 @@ static void gd_vc_adjustment_changed(GtkAdjustment *adjustment, void *opaque)
     }
 }
 
-static int gd_vc_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+static int gd_vc_chr_write(Chardev *chr, const uint8_t *buf, int len)
 {
-    VCDriverState *vcd = (VCDriverState *)chr;
+    VCChardev *vcd = (VCChardev *)chr;
     VirtualConsole *vc = vcd->console;
 
     vte_terminal_feed(VTE_TERMINAL(vc->vte.terminal), (const char *)buf, len);
     return len;
 }
 
-static void gd_vc_chr_set_echo(CharDriverState *chr, bool echo)
+static void gd_vc_chr_set_echo(Chardev *chr, bool echo)
 {
-    VCDriverState *vcd = (VCDriverState *)chr;
+    VCChardev *vcd = (VCChardev *)chr;
     VirtualConsole *vc = vcd->console;
 
     if (vc) {
@@ -1711,19 +1711,19 @@ static void gd_vc_chr_set_echo(CharDriverState *chr, bool echo)
 }
 
 static int nb_vcs;
-static CharDriverState *vcs[MAX_VCS];
+static Chardev *vcs[MAX_VCS];
 
-static CharDriverState *gd_vc_handler(ChardevVC *vc, Error **errp)
+static Chardev *gd_vc_handler(ChardevVC *vc, Error **errp)
 {
     static const CharDriver gd_vc_driver = {
-        .instance_size = sizeof(VCDriverState),
+        .instance_size = sizeof(VCChardev),
         .kind = CHARDEV_BACKEND_KIND_VC,
         .chr_write = gd_vc_chr_write,
         .chr_set_echo = gd_vc_chr_set_echo,
     };
 
     ChardevCommon *common = qapi_ChardevVC_base(vc);
-    CharDriverState *chr;
+    Chardev *chr;
 
     if (nb_vcs == MAX_VCS) {
         error_setg(errp, "Maximum number of consoles reached");
@@ -1768,14 +1768,14 @@ static gboolean gd_vc_in(VteTerminal *terminal, gchar *text, guint size,
 }
 
 static GSList *gd_vc_vte_init(GtkDisplayState *s, VirtualConsole *vc,
-                              CharDriverState *chr, int idx,
+                              Chardev *chr, int idx,
                               GSList *group, GtkWidget *view_menu)
 {
     char buffer[32];
     GtkWidget *box;
     GtkWidget *scrollbar;
     GtkAdjustment *vadjustment;
-    VCDriverState *vcd = (VCDriverState *)chr;
+    VCChardev *vcd = (VCChardev *)chr;
 
     vc->s = s;
     vc->vte.echo = vcd->echo;
diff --git a/vl.c b/vl.c
index bc8e837..41be59f 100644
--- a/vl.c
+++ b/vl.c
@@ -151,10 +151,10 @@ static int full_screen = 0;
 static int no_frame = 0;
 int no_quit = 0;
 static bool grab_on_hover;
-CharDriverState *serial_hds[MAX_SERIAL_PORTS];
-CharDriverState *parallel_hds[MAX_PARALLEL_PORTS];
-CharDriverState *virtcon_hds[MAX_VIRTIO_CONSOLES];
-CharDriverState *sclp_hds[MAX_SCLP_CONSOLES];
+Chardev *serial_hds[MAX_SERIAL_PORTS];
+Chardev *parallel_hds[MAX_PARALLEL_PORTS];
+Chardev *virtcon_hds[MAX_VIRTIO_CONSOLES];
+Chardev *sclp_hds[MAX_SCLP_CONSOLES];
 int win2k_install_hack = 0;
 int singlestep = 0;
 int smp_cpus = 1;
@@ -2321,7 +2321,7 @@ static int fsdev_init_func(void *opaque, QemuOpts *opts, Error **errp)
 
 static int mon_init_func(void *opaque, QemuOpts *opts, Error **errp)
 {
-    CharDriverState *chr;
+    Chardev *chr;
     const char *chardev;
     const char *mode;
     int flags;
diff --git a/xen-common-stub.c b/xen-common-stub.c
index 699c3f1..09fce2d 100644
--- a/xen-common-stub.c
+++ b/xen-common-stub.c
@@ -9,6 +9,6 @@
 #include "qemu-common.h"
 #include "hw/xen/xen.h"
 
-void xenstore_store_pv_console_info(int i, CharDriverState *chr)
+void xenstore_store_pv_console_info(int i, Chardev *chr)
 {
 }
diff --git a/xen-common.c b/xen-common.c
index 9099760..fd2c928 100644
--- a/xen-common.c
+++ b/xen-common.c
@@ -25,7 +25,7 @@
     do { } while (0)
 #endif
 
-static int store_dev_info(int domid, CharDriverState *cs, const char *string)
+static int store_dev_info(int domid, Chardev *cs, const char *string)
 {
     struct xs_handle *xs = NULL;
     char *path = NULL;
@@ -74,7 +74,7 @@ out:
     return ret;
 }
 
-void xenstore_store_pv_console_info(int i, CharDriverState *chr)
+void xenstore_store_pv_console_info(int i, Chardev *chr)
 {
     if (i == 0) {
         store_dev_info(xen_domid, chr, "/console");
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [Qemu-devel] [PULL 34/41] char: rename TCPChardev and NetChardev
  2017-01-27 13:45 [Qemu-devel] [PULL 00/41] Misc changes for 2017-01-27 Paolo Bonzini
                   ` (32 preceding siblings ...)
  2017-01-27 13:45 ` [Qemu-devel] [PULL 33/41] char: rename CharDriverState Chardev Paolo Bonzini
@ 2017-01-27 13:45 ` Paolo Bonzini
  2017-01-27 13:45 ` [Qemu-devel] [PULL 35/41] spice-char: improve error reporting Paolo Bonzini
                   ` (7 subsequent siblings)
  41 siblings, 0 replies; 43+ messages in thread
From: Paolo Bonzini @ 2017-01-27 13:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Rename the types to follow the name of the chardev kind.
- socket: TCPChardev -> SocketChardev
- udp: NetChardev -> UdpChardev

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 qemu-char.c | 74 ++++++++++++++++++++++++++++++-------------------------------
 1 file changed, 37 insertions(+), 37 deletions(-)

diff --git a/qemu-char.c b/qemu-char.c
index f828e61..c82d6d2 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -2691,12 +2691,12 @@ typedef struct {
     int bufcnt;
     int bufptr;
     int max_size;
-} NetChardev;
+} UdpChardev;
 
 /* Called with chr_write_lock held.  */
 static int udp_chr_write(Chardev *chr, const uint8_t *buf, int len)
 {
-    NetChardev *s = (NetChardev *)chr;
+    UdpChardev *s = (UdpChardev *)chr;
 
     return qio_channel_write(
         s->ioc, (const char *)buf, len, NULL);
@@ -2705,7 +2705,7 @@ static int udp_chr_write(Chardev *chr, const uint8_t *buf, int len)
 static int udp_chr_read_poll(void *opaque)
 {
     Chardev *chr = opaque;
-    NetChardev *s = opaque;
+    UdpChardev *s = opaque;
 
     s->max_size = qemu_chr_be_can_write(chr);
 
@@ -2723,7 +2723,7 @@ static int udp_chr_read_poll(void *opaque)
 static gboolean udp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
 {
     Chardev *chr = opaque;
-    NetChardev *s = opaque;
+    UdpChardev *s = opaque;
     ssize_t ret;
 
     if (s->max_size == 0) {
@@ -2750,7 +2750,7 @@ static gboolean udp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
 static void udp_chr_update_read_handler(Chardev *chr,
                                         GMainContext *context)
 {
-    NetChardev *s = (NetChardev *)chr;
+    UdpChardev *s = (UdpChardev *)chr;
 
     remove_fd_in_watch(chr);
     if (s->ioc) {
@@ -2763,7 +2763,7 @@ static void udp_chr_update_read_handler(Chardev *chr,
 
 static void udp_chr_free(Chardev *chr)
 {
-    NetChardev *s = (NetChardev *)chr;
+    UdpChardev *s = (UdpChardev *)chr;
 
     remove_fd_in_watch(chr);
     if (s->ioc) {
@@ -2799,13 +2799,13 @@ typedef struct {
     guint reconnect_timer;
     int64_t reconnect_time;
     bool connect_err_reported;
-} TCPChardev;
+} SocketChardev;
 
 static gboolean socket_reconnect_timeout(gpointer opaque);
 
 static void qemu_chr_socket_restart_timer(Chardev *chr)
 {
-    TCPChardev *s = (TCPChardev *)chr;
+    SocketChardev *s = (SocketChardev *)chr;
     char *name;
 
     assert(s->connected == 0);
@@ -2819,7 +2819,7 @@ static void qemu_chr_socket_restart_timer(Chardev *chr)
 static void check_report_connect_error(Chardev *chr,
                                        Error *err)
 {
-    TCPChardev *s = (TCPChardev *)chr;
+    SocketChardev *s = (SocketChardev *)chr;
 
     if (!s->connect_err_reported) {
         error_report("Unable to connect character device %s: %s",
@@ -2836,7 +2836,7 @@ static gboolean tcp_chr_accept(QIOChannel *chan,
 /* Called with chr_write_lock held.  */
 static int tcp_chr_write(Chardev *chr, const uint8_t *buf, int len)
 {
-    TCPChardev *s = (TCPChardev *)chr;
+    SocketChardev *s = (SocketChardev *)chr;
 
     if (s->connected) {
         int ret =  io_channel_send_full(s->ioc, buf, len,
@@ -2860,7 +2860,7 @@ static int tcp_chr_write(Chardev *chr, const uint8_t *buf, int len)
 static int tcp_chr_read_poll(void *opaque)
 {
     Chardev *chr = opaque;
-    TCPChardev *s = opaque;
+    SocketChardev *s = opaque;
     if (!s->connected)
         return 0;
     s->max_size = qemu_chr_be_can_write(chr);
@@ -2870,7 +2870,7 @@ static int tcp_chr_read_poll(void *opaque)
 #define IAC 255
 #define IAC_BREAK 243
 static void tcp_chr_process_IAC_bytes(Chardev *chr,
-                                      TCPChardev *s,
+                                      SocketChardev *s,
                                       uint8_t *buf, int *size)
 {
     /* Handle any telnet client's basic IAC options to satisfy char by
@@ -2919,7 +2919,7 @@ static void tcp_chr_process_IAC_bytes(Chardev *chr,
 
 static int tcp_get_msgfds(Chardev *chr, int *fds, int num)
 {
-    TCPChardev *s = (TCPChardev *)chr;
+    SocketChardev *s = (SocketChardev *)chr;
 
     int to_copy = (s->read_msgfds_num < num) ? s->read_msgfds_num : num;
 
@@ -2945,7 +2945,7 @@ static int tcp_get_msgfds(Chardev *chr, int *fds, int num)
 
 static int tcp_set_msgfds(Chardev *chr, int *fds, int num)
 {
-    TCPChardev *s = (TCPChardev *)chr;
+    SocketChardev *s = (SocketChardev *)chr;
 
     /* clear old pending fd array */
     g_free(s->write_msgfds);
@@ -2970,7 +2970,7 @@ static int tcp_set_msgfds(Chardev *chr, int *fds, int num)
 
 static ssize_t tcp_chr_recv(Chardev *chr, char *buf, size_t len)
 {
-    TCPChardev *s = (TCPChardev *)chr;
+    SocketChardev *s = (SocketChardev *)chr;
     struct iovec iov = { .iov_base = buf, .iov_len = len };
     int ret;
     size_t i;
@@ -3027,13 +3027,13 @@ static ssize_t tcp_chr_recv(Chardev *chr, char *buf, size_t len)
 
 static GSource *tcp_chr_add_watch(Chardev *chr, GIOCondition cond)
 {
-    TCPChardev *s = (TCPChardev *)chr;
+    SocketChardev *s = (SocketChardev *)chr;
     return qio_channel_create_watch(s->ioc, cond);
 }
 
 static void tcp_chr_free_connection(Chardev *chr)
 {
-    TCPChardev *s = (TCPChardev *)chr;
+    SocketChardev *s = (SocketChardev *)chr;
     int i;
 
     if (!s->connected) {
@@ -3062,7 +3062,7 @@ static void tcp_chr_free_connection(Chardev *chr)
 
 static void tcp_chr_disconnect(Chardev *chr)
 {
-    TCPChardev *s = (TCPChardev *)chr;
+    SocketChardev *s = (SocketChardev *)chr;
 
     if (!s->connected) {
         return;
@@ -3085,7 +3085,7 @@ static void tcp_chr_disconnect(Chardev *chr)
 static gboolean tcp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
 {
     Chardev *chr = opaque;
-    TCPChardev *s = opaque;
+    SocketChardev *s = opaque;
     uint8_t buf[READ_BUF_LEN];
     int len, size;
 
@@ -3111,7 +3111,7 @@ static gboolean tcp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
 
 static int tcp_chr_sync_read(Chardev *chr, const uint8_t *buf, int len)
 {
-    TCPChardev *s = (TCPChardev *)chr;
+    SocketChardev *s = (SocketChardev *)chr;
     int size;
 
     if (!s->connected) {
@@ -3130,7 +3130,7 @@ static int tcp_chr_sync_read(Chardev *chr, const uint8_t *buf, int len)
 static void tcp_chr_connect(void *opaque)
 {
     Chardev *chr = opaque;
-    TCPChardev *s = opaque;
+    SocketChardev *s = opaque;
 
     g_free(chr->filename);
     chr->filename = sockaddr_to_str(
@@ -3151,7 +3151,7 @@ static void tcp_chr_connect(void *opaque)
 static void tcp_chr_update_read_handler(Chardev *chr,
                                         GMainContext *context)
 {
-    TCPChardev *s = (TCPChardev *)chr;
+    SocketChardev *s = (SocketChardev *)chr;
 
     if (!s->connected) {
         return;
@@ -3202,7 +3202,7 @@ static gboolean tcp_chr_telnet_init_io(QIOChannel *ioc,
 
 static void tcp_chr_telnet_init(Chardev *chr)
 {
-    TCPChardev *s = (TCPChardev *)chr;
+    SocketChardev *s = (SocketChardev *)chr;
     TCPCharDriverTelnetInit *init =
         g_new0(TCPCharDriverTelnetInit, 1);
     size_t n = 0;
@@ -3237,7 +3237,7 @@ static void tcp_chr_tls_handshake(QIOTask *task,
                                   gpointer user_data)
 {
     Chardev *chr = user_data;
-    TCPChardev *s = user_data;
+    SocketChardev *s = user_data;
 
     if (qio_task_propagate_error(task, NULL)) {
         tcp_chr_disconnect(chr);
@@ -3253,7 +3253,7 @@ static void tcp_chr_tls_handshake(QIOTask *task,
 
 static void tcp_chr_tls_init(Chardev *chr)
 {
-    TCPChardev *s = (TCPChardev *)chr;
+    SocketChardev *s = (SocketChardev *)chr;
     QIOChannelTLS *tioc;
     Error *err = NULL;
     gchar *name;
@@ -3292,7 +3292,7 @@ static void tcp_chr_tls_init(Chardev *chr)
 static void tcp_chr_set_client_ioc_name(Chardev *chr,
                                         QIOChannelSocket *sioc)
 {
-    TCPChardev *s = (TCPChardev *)chr;
+    SocketChardev *s = (SocketChardev *)chr;
     char *name;
     name = g_strdup_printf("chardev-tcp-%s-%s",
                            s->is_listen ? "server" : "client",
@@ -3304,7 +3304,7 @@ static void tcp_chr_set_client_ioc_name(Chardev *chr,
 
 static int tcp_chr_new_client(Chardev *chr, QIOChannelSocket *sioc)
 {
-    TCPChardev *s = (TCPChardev *)chr;
+    SocketChardev *s = (SocketChardev *)chr;
 
     if (s->ioc != NULL) {
 	return -1;
@@ -3376,7 +3376,7 @@ static gboolean tcp_chr_accept(QIOChannel *channel,
 
 static int tcp_chr_wait_connected(Chardev *chr, Error **errp)
 {
-    TCPChardev *s = (TCPChardev *)chr;
+    SocketChardev *s = (SocketChardev *)chr;
     QIOChannelSocket *sioc;
 
     /* It can't wait on s->connected, since it is set asynchronously
@@ -3424,7 +3424,7 @@ int qemu_chr_fe_wait_connected(CharBackend *be, Error **errp)
 
 static void tcp_chr_free(Chardev *chr)
 {
-    TCPChardev *s = (TCPChardev *)chr;
+    SocketChardev *s = (SocketChardev *)chr;
 
     tcp_chr_free_connection(chr);
 
@@ -3452,7 +3452,7 @@ static void qemu_chr_socket_connected(QIOTask *task, void *opaque)
 {
     QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(qio_task_get_source(task));
     Chardev *chr = opaque;
-    TCPChardev *s = (TCPChardev *)chr;
+    SocketChardev *s = (SocketChardev *)chr;
     Error *err = NULL;
 
     if (qio_task_propagate_error(task, &err)) {
@@ -4733,7 +4733,7 @@ static const CharDriver serial_driver = {
 static gboolean socket_reconnect_timeout(gpointer opaque)
 {
     Chardev *chr = opaque;
-    TCPChardev *s = opaque;
+    SocketChardev *s = opaque;
     QIOChannelSocket *sioc;
 
     s->reconnect_timer = 0;
@@ -4759,7 +4759,7 @@ static Chardev *qmp_chardev_open_socket(const CharDriver *driver,
                                         Error **errp)
 {
     Chardev *chr;
-    TCPChardev *s;
+    SocketChardev *s;
     ChardevSocket *sock = backend->u.socket.data;
     SocketAddress *addr = sock->addr;
     bool do_nodelay     = sock->has_nodelay ? sock->nodelay : false;
@@ -4774,7 +4774,7 @@ static Chardev *qmp_chardev_open_socket(const CharDriver *driver,
     if (!chr) {
         return NULL;
     }
-    s = (TCPChardev *)chr;
+    s = (SocketChardev *)chr;
 
     s->is_unix = addr->type == SOCKET_ADDRESS_KIND_UNIX;
     s->is_listen = is_listen;
@@ -4881,7 +4881,7 @@ static Chardev *qmp_chardev_open_socket(const CharDriver *driver,
 }
 
 static const CharDriver socket_driver = {
-    .instance_size = sizeof(TCPChardev),
+    .instance_size = sizeof(SocketChardev),
     .kind = CHARDEV_BACKEND_KIND_SOCKET,
     .parse = qemu_chr_parse_socket,
     .create = qmp_chardev_open_socket,
@@ -4909,7 +4909,7 @@ static Chardev *qmp_chardev_open_udp(const CharDriver *driver,
     QIOChannelSocket *sioc = qio_channel_socket_new();
     char *name;
     Chardev *chr;
-    NetChardev *s;
+    UdpChardev *s;
 
     if (qio_channel_socket_dgram_sync(sioc,
                                       udp->local, udp->remote,
@@ -4927,7 +4927,7 @@ static Chardev *qmp_chardev_open_udp(const CharDriver *driver,
     qio_channel_set_name(QIO_CHANNEL(sioc), name);
     g_free(name);
 
-    s = (NetChardev *)chr;
+    s = (UdpChardev *)chr;
     s->ioc = QIO_CHANNEL(sioc);
     /* be isn't opened until we get a connection */
     *be_opened = false;
@@ -4936,7 +4936,7 @@ static Chardev *qmp_chardev_open_udp(const CharDriver *driver,
 }
 
 static const CharDriver udp_driver = {
-    .instance_size = sizeof(NetChardev),
+    .instance_size = sizeof(UdpChardev),
     .kind = CHARDEV_BACKEND_KIND_UDP,
     .parse = qemu_chr_parse_udp,
     .create = qmp_chardev_open_udp,
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [Qemu-devel] [PULL 35/41] spice-char: improve error reporting
  2017-01-27 13:45 [Qemu-devel] [PULL 00/41] Misc changes for 2017-01-27 Paolo Bonzini
                   ` (33 preceding siblings ...)
  2017-01-27 13:45 ` [Qemu-devel] [PULL 34/41] char: rename TCPChardev and NetChardev Paolo Bonzini
@ 2017-01-27 13:45 ` Paolo Bonzini
  2017-01-27 13:45 ` [Qemu-devel] [PULL 36/41] char: use error_report() Paolo Bonzini
                   ` (6 subsequent siblings)
  41 siblings, 0 replies; 43+ messages in thread
From: Paolo Bonzini @ 2017-01-27 13:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Set errp to report errors up to the right monitor.

Use error_append_hint() to give hints about parameters on !qmp monitors,
instead of a direct fprintf() call.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 spice-qemu-char.c | 30 ++++++++++--------------------
 1 file changed, 10 insertions(+), 20 deletions(-)

diff --git a/spice-qemu-char.c b/spice-qemu-char.c
index 38b1f6c..5e5897b 100644
--- a/spice-qemu-char.c
+++ b/spice-qemu-char.c
@@ -2,6 +2,7 @@
 #include "trace.h"
 #include "ui/qemu-spice.h"
 #include "sysemu/char.h"
+#include "qemu/error-report.h"
 #include <spice.h>
 #include <spice/protocol.h>
 
@@ -239,23 +240,6 @@ static void spice_port_set_fe_open(struct Chardev *chr, int fe_open)
 #endif
 }
 
-static void print_allowed_subtypes(void)
-{
-    const char** psubtype;
-    int i;
-
-    fprintf(stderr, "allowed names: ");
-    for(i=0, psubtype = spice_server_char_device_recognized_subtypes();
-        *psubtype != NULL; ++psubtype, ++i) {
-        if (i == 0) {
-            fprintf(stderr, "%s", *psubtype);
-        } else {
-            fprintf(stderr, ", %s", *psubtype);
-        }
-    }
-    fprintf(stderr, "\n");
-}
-
 static void spice_chr_accept_input(struct Chardev *chr)
 {
     SpiceChardev *s = (SpiceChardev *)chr;
@@ -302,8 +286,14 @@ static Chardev *qemu_chr_open_spice_vmc(const CharDriver *driver,
         }
     }
     if (*psubtype == NULL) {
-        fprintf(stderr, "spice-qemu-char: unsupported type: %s\n", type);
-        print_allowed_subtypes();
+        char *subtypes = g_strjoinv(", ",
+            (gchar **)spice_server_char_device_recognized_subtypes());
+
+        error_setg(errp, "unsupported type name: %s", type);
+        error_append_hint(errp, "allowed spice char type names: %s\n",
+                          subtypes);
+
+        g_free(subtypes);
         return NULL;
     }
 
@@ -326,7 +316,7 @@ static Chardev *qemu_chr_open_spice_port(const CharDriver *driver,
     SpiceChardev *s;
 
     if (name == NULL) {
-        fprintf(stderr, "spice-qemu-char: missing name parameter\n");
+        error_setg(errp, "missing name parameter");
         return NULL;
     }
 
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [Qemu-devel] [PULL 36/41] char: use error_report()
  2017-01-27 13:45 [Qemu-devel] [PULL 00/41] Misc changes for 2017-01-27 Paolo Bonzini
                   ` (34 preceding siblings ...)
  2017-01-27 13:45 ` [Qemu-devel] [PULL 35/41] spice-char: improve error reporting Paolo Bonzini
@ 2017-01-27 13:45 ` Paolo Bonzini
  2017-01-27 13:45 ` [Qemu-devel] [PULL 37/41] gtk: overwrite the console.c char driver Paolo Bonzini
                   ` (5 subsequent siblings)
  41 siblings, 0 replies; 43+ messages in thread
From: Paolo Bonzini @ 2017-01-27 13:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Prefer error_report() over fprintf(stderr..)

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 qemu-char.c | 25 ++++++++++++++-----------
 1 file changed, 14 insertions(+), 11 deletions(-)

diff --git a/qemu-char.c b/qemu-char.c
index c82d6d2..113c4a8 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -451,8 +451,8 @@ int qemu_chr_fe_get_msgfd(CharBackend *be)
     int fd;
     int res = (qemu_chr_fe_get_msgfds(be, &fd, 1) == 1) ? fd : -1;
     if (s && qemu_chr_replay(s)) {
-        fprintf(stderr,
-                "Replay: get msgfd is not supported for serial devices yet\n");
+        error_report("Replay: get msgfd is not supported "
+                     "for serial devices yet");
         exit(1);
     }
     return res;
@@ -1680,8 +1680,8 @@ static Chardev *qemu_chr_open_pty(const CharDriver *driver,
     ret->pty = g_strdup(pty_name);
     ret->has_pty = true;
 
-    fprintf(stderr, "char device redirected to %s (label %s)\n",
-            pty_name, id);
+    error_report("char device redirected to %s (label %s)",
+                 pty_name, id);
 
     s = (PtyChardev *)chr;
     s->ioc = QIO_CHANNEL(qio_channel_file_new_fd(master_fd));
@@ -3383,8 +3383,8 @@ static int tcp_chr_wait_connected(Chardev *chr, Error **errp)
      * in TLS and telnet cases, only wait for an accepted socket */
     while (!s->ioc) {
         if (s->is_listen) {
-            fprintf(stderr, "QEMU waiting for connection on: %s\n",
-                    chr->filename);
+            error_report("QEMU waiting for connection on: %s",
+                         chr->filename);
             qio_channel_set_blocking(QIO_CHANNEL(s->listen_ioc), true, NULL);
             tcp_chr_accept(QIO_CHANNEL(s->listen_ioc), G_IO_IN, chr);
             qio_channel_set_blocking(QIO_CHANNEL(s->listen_ioc), false, NULL);
@@ -4146,16 +4146,19 @@ Chardev *qemu_chr_new_from_opts(QemuOpts *opts,
     }
 
     if (is_help_option(name)) {
-        fprintf(stderr, "Available chardev backend types:\n");
+        GString *str = g_string_new("");
         for (i = 0; i < ARRAY_SIZE(backends); i++) {
             cd = backends[i];
             if (cd) {
-                fprintf(stderr, "%s\n", ChardevBackendKind_lookup[cd->kind]);
+                g_string_append_printf(str, "\n%s", ChardevBackendKind_lookup[cd->kind]);
                 if (cd->alias) {
-                    fprintf(stderr, "%s\n", cd->alias);
+                    g_string_append_printf(str, "\n%s", cd->alias);
                 }
             }
         }
+
+        error_report("Available chardev backend types: %s", str->str);
+        g_string_free(str, true);
         exit(0);
     }
 
@@ -4267,8 +4270,8 @@ Chardev *qemu_chr_new(const char *label, const char *filename)
             qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_REPLAY);
         }
         if (qemu_chr_replay(chr) && chr->driver->chr_ioctl) {
-            fprintf(stderr,
-                    "Replay: ioctl is not supported for serial devices yet\n");
+            error_report("Replay: ioctl is not supported "
+                         "for serial devices yet");
         }
         replay_register_char_driver(chr);
     }
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [Qemu-devel] [PULL 37/41] gtk: overwrite the console.c char driver
  2017-01-27 13:45 [Qemu-devel] [PULL 00/41] Misc changes for 2017-01-27 Paolo Bonzini
                   ` (35 preceding siblings ...)
  2017-01-27 13:45 ` [Qemu-devel] [PULL 36/41] char: use error_report() Paolo Bonzini
@ 2017-01-27 13:45 ` Paolo Bonzini
  2017-01-27 13:45 ` [Qemu-devel] [PULL 38/41] baum: use a common prefix for chr callbacks Paolo Bonzini
                   ` (4 subsequent siblings)
  41 siblings, 0 replies; 43+ messages in thread
From: Paolo Bonzini @ 2017-01-27 13:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Instead of registering a vc handler to allocate the Gtk VC Chardev,
overwrite the console.c char driver.

A later patch, when switching to QOM, will register a default console vc
QOM class if none has been registered before.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 include/sysemu/char.h |  4 +---
 ui/console.c          | 24 +++++++-----------------
 ui/gtk.c              | 30 +++++++++++++++++++++---------
 3 files changed, 29 insertions(+), 29 deletions(-)

diff --git a/include/sysemu/char.h b/include/sysemu/char.h
index 3a82d80..baa9e8f 100644
--- a/include/sysemu/char.h
+++ b/include/sysemu/char.h
@@ -507,9 +507,7 @@ void register_char_driver(const CharDriver *driver);
 
 extern int term_escape_char;
 
-
 /* console.c */
-typedef Chardev *(VcHandler)(ChardevVC *vc, Error **errp);
-void register_vc_handler(VcHandler *handler);
+void qemu_chr_parse_vc(QemuOpts *opts, ChardevBackend *backend, Error **errp);
 
 #endif
diff --git a/ui/console.c b/ui/console.c
index 7420f28..d8bc8a6 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -2058,8 +2058,12 @@ static void text_console_do_init(Chardev *chr, DisplayState *ds)
 
 static const CharDriver vc_driver;
 
-static Chardev *text_console_init(ChardevVC *vc, Error **errp)
+static Chardev *vc_init(const CharDriver *driver,
+                        const char *id, ChardevBackend *backend,
+                        ChardevReturn *ret, bool *be_opened,
+                        Error **errp)
 {
+    ChardevVC *vc = backend->u.vc.data;
     ChardevCommon *common = qapi_ChardevVC_base(vc);
     Chardev *chr;
     VCChardev *drv;
@@ -2105,26 +2109,13 @@ static Chardev *text_console_init(ChardevVC *vc, Error **errp)
     if (display_state) {
         text_console_do_init(chr, display_state);
     }
-    return chr;
-}
 
-static VcHandler *vc_handler = text_console_init;
-
-static Chardev *vc_init(const CharDriver *driver,
-                        const char *id, ChardevBackend *backend,
-                        ChardevReturn *ret, bool *be_opened,
-                        Error **errp)
-{
     /* console/chardev init sometimes completes elsewhere in a 2nd
      * stage, so defer OPENED events until they are fully initialized
      */
     *be_opened = false;
-    return vc_handler(backend->u.vc.data, errp);
-}
 
-void register_vc_handler(VcHandler *handler)
-{
-    vc_handler = handler;
+    return chr;
 }
 
 void qemu_console_resize(QemuConsole *s, int width, int height)
@@ -2162,8 +2153,7 @@ PixelFormat qemu_default_pixelformat(int bpp)
     return pf;
 }
 
-static void qemu_chr_parse_vc(QemuOpts *opts, ChardevBackend *backend,
-                              Error **errp)
+void qemu_chr_parse_vc(QemuOpts *opts, ChardevBackend *backend, Error **errp)
 {
     int val;
     ChardevVC *vc;
diff --git a/ui/gtk.c b/ui/gtk.c
index 175871a..04df0ad 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -1712,16 +1712,14 @@ static void gd_vc_chr_set_echo(Chardev *chr, bool echo)
 
 static int nb_vcs;
 static Chardev *vcs[MAX_VCS];
+static const CharDriver gd_vc_driver;
 
-static Chardev *gd_vc_handler(ChardevVC *vc, Error **errp)
+static Chardev *vc_init(const CharDriver *driver,
+                        const char *id, ChardevBackend *backend,
+                        ChardevReturn *ret, bool *be_opened,
+                        Error **errp)
 {
-    static const CharDriver gd_vc_driver = {
-        .instance_size = sizeof(VCChardev),
-        .kind = CHARDEV_BACKEND_KIND_VC,
-        .chr_write = gd_vc_chr_write,
-        .chr_set_echo = gd_vc_chr_set_echo,
-    };
-
+    ChardevVC *vc = backend->u.vc.data;
     ChardevCommon *common = qapi_ChardevVC_base(vc);
     Chardev *chr;
 
@@ -1737,9 +1735,22 @@ static Chardev *gd_vc_handler(ChardevVC *vc, Error **errp)
 
     vcs[nb_vcs++] = chr;
 
+    /* console/chardev init sometimes completes elsewhere in a 2nd
+     * stage, so defer OPENED events until they are fully initialized
+     */
+    *be_opened = false;
+
     return chr;
 }
 
+static const CharDriver gd_vc_driver = {
+    .instance_size = sizeof(VCChardev),
+    .kind = CHARDEV_BACKEND_KIND_VC,
+    .parse = qemu_chr_parse_vc, .create = vc_init,
+    .chr_write = gd_vc_chr_write,
+    .chr_set_echo = gd_vc_chr_set_echo,
+};
+
 static gboolean gd_vc_in(VteTerminal *terminal, gchar *text, guint size,
                          gpointer user_data)
 {
@@ -2336,6 +2347,7 @@ void early_gtk_display_init(int opengl)
     }
 
 #if defined(CONFIG_VTE)
-    register_vc_handler(gd_vc_handler);
+    /* overwrite the console.c vc driver */
+    register_char_driver(&gd_vc_driver);
 #endif
 }
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [Qemu-devel] [PULL 38/41] baum: use a common prefix for chr callbacks
  2017-01-27 13:45 [Qemu-devel] [PULL 00/41] Misc changes for 2017-01-27 Paolo Bonzini
                   ` (36 preceding siblings ...)
  2017-01-27 13:45 ` [Qemu-devel] [PULL 37/41] gtk: overwrite the console.c char driver Paolo Bonzini
@ 2017-01-27 13:45 ` Paolo Bonzini
  2017-01-27 13:45 ` [Qemu-devel] [PULL 39/41] vc: " Paolo Bonzini
                   ` (3 subsequent siblings)
  41 siblings, 0 replies; 43+ messages in thread
From: Paolo Bonzini @ 2017-01-27 13:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 backends/baum.c | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/backends/baum.c b/backends/baum.c
index 23d3c4a..8842936 100644
--- a/backends/baum.c
+++ b/backends/baum.c
@@ -253,7 +253,7 @@ static int baum_deferred_init(BaumChardev *baum)
 }
 
 /* The serial port can receive more of our data */
-static void baum_accept_input(struct Chardev *chr)
+static void baum_chr_accept_input(struct Chardev *chr)
 {
     BaumChardev *baum = (BaumChardev *)chr;
     int room, first;
@@ -470,7 +470,7 @@ static int baum_eat_packet(BaumChardev *baum, const uint8_t *buf, int len)
 }
 
 /* The other end is writing some data.  Store it and try to interpret */
-static int baum_write(Chardev *chr, const uint8_t *buf, int len)
+static int baum_chr_write(Chardev *chr, const uint8_t *buf, int len)
 {
     BaumChardev *baum = (BaumChardev *)chr;
     int tocopy, cur, eaten, orig_len = len;
@@ -613,7 +613,7 @@ static void baum_chr_read(void *opaque)
     }
 }
 
-static void baum_free(struct Chardev *chr)
+static void baum_chr_free(struct Chardev *chr)
 {
     BaumChardev *baum = (BaumChardev *)chr;
 
@@ -624,7 +624,7 @@ static void baum_free(struct Chardev *chr)
     }
 }
 
-static Chardev *chr_baum_init(const CharDriver *driver,
+static Chardev *baum_chr_init(const CharDriver *driver,
                               const char *id,
                               ChardevBackend *backend,
                               ChardevReturn *ret,
@@ -670,10 +670,10 @@ static void register_types(void)
     static const CharDriver driver = {
         .instance_size = sizeof(BaumChardev),
         .kind = CHARDEV_BACKEND_KIND_BRAILLE,
-        .create = chr_baum_init,
-        .chr_write = baum_write,
-        .chr_accept_input = baum_accept_input,
-        .chr_free = baum_free,
+        .create = baum_chr_init,
+        .chr_write = baum_chr_write,
+        .chr_accept_input = baum_chr_accept_input,
+        .chr_free = baum_chr_free,
     };
 
     register_char_driver(&driver);
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [Qemu-devel] [PULL 39/41] vc: use a common prefix for chr callbacks
  2017-01-27 13:45 [Qemu-devel] [PULL 00/41] Misc changes for 2017-01-27 Paolo Bonzini
                   ` (37 preceding siblings ...)
  2017-01-27 13:45 ` [Qemu-devel] [PULL 38/41] baum: use a common prefix for chr callbacks Paolo Bonzini
@ 2017-01-27 13:45 ` Paolo Bonzini
  2017-01-27 13:45 ` [Qemu-devel] [PULL 40/41] chardev: qom-ify Paolo Bonzini
                   ` (2 subsequent siblings)
  41 siblings, 0 replies; 43+ messages in thread
From: Paolo Bonzini @ 2017-01-27 13:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau

From: Marc-André Lureau <marcandre.lureau@redhat.com>

vc_chr_write() is more appropriate than _puts() since no newline is
appended, even though it's not used only as a callback.

Keep "qemu_chr_parse" prefix, most chardev parse functions use this
prefix atm.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 ui/console.c | 24 ++++++++++++------------
 1 file changed, 12 insertions(+), 12 deletions(-)

diff --git a/ui/console.c b/ui/console.c
index d8bc8a6..c8ee164 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -1051,7 +1051,7 @@ typedef struct VCChardev {
     QemuConsole *console;
 } VCChardev;
 
-static int console_puts(Chardev *chr, const uint8_t *buf, int len)
+static int vc_chr_write(Chardev *chr, const uint8_t *buf, int len)
 {
     VCChardev *drv = (VCChardev *)chr;
     QemuConsole *s = drv->console;
@@ -1139,13 +1139,13 @@ void kbd_put_keysym_console(QemuConsole *s, int keysym)
             *q++ = '[';
             *q++ = keysym & 0xff;
         } else if (s->echo && (keysym == '\r' || keysym == '\n')) {
-            console_puts(s->chr, (const uint8_t *) "\r", 1);
+            vc_chr_write(s->chr, (const uint8_t *) "\r", 1);
             *q++ = '\n';
         } else {
             *q++ = keysym;
         }
         if (s->echo) {
-            console_puts(s->chr, buf, q - buf);
+            vc_chr_write(s->chr, buf, q - buf);
         }
         be = s->chr->be;
         if (be && be->chr_read) {
@@ -1962,7 +1962,7 @@ int qemu_console_get_height(QemuConsole *con, int fallback)
     return con ? surface_height(con->surface) : fallback;
 }
 
-static void text_console_set_echo(Chardev *chr, bool echo)
+static void vc_chr_set_echo(Chardev *chr, bool echo)
 {
     VCChardev *drv = (VCChardev *)chr;
     QemuConsole *s = drv->console;
@@ -2049,7 +2049,7 @@ static void text_console_do_init(Chardev *chr, DisplayState *ds)
 
         s->t_attrib.bgcol = QEMU_COLOR_BLUE;
         len = snprintf(msg, sizeof(msg), "%s console\r\n", chr->label);
-        console_puts(chr, (uint8_t*)msg, len);
+        vc_chr_write(chr, (uint8_t *)msg, len);
         s->t_attrib = s->t_attrib_default;
     }
 
@@ -2058,10 +2058,10 @@ static void text_console_do_init(Chardev *chr, DisplayState *ds)
 
 static const CharDriver vc_driver;
 
-static Chardev *vc_init(const CharDriver *driver,
-                        const char *id, ChardevBackend *backend,
-                        ChardevReturn *ret, bool *be_opened,
-                        Error **errp)
+static Chardev *vc_chr_init(const CharDriver *driver,
+                            const char *id, ChardevBackend *backend,
+                            ChardevReturn *ret, bool *be_opened,
+                            Error **errp)
 {
     ChardevVC *vc = backend->u.vc.data;
     ChardevCommon *common = qapi_ChardevVC_base(vc);
@@ -2197,9 +2197,9 @@ static const CharDriver vc_driver = {
     .instance_size = sizeof(VCChardev),
     .kind = CHARDEV_BACKEND_KIND_VC,
     .parse = qemu_chr_parse_vc,
-    .create = vc_init,
-    .chr_write = console_puts,
-    .chr_set_echo = text_console_set_echo,
+    .create = vc_chr_init,
+    .chr_write = vc_chr_write,
+    .chr_set_echo = vc_chr_set_echo,
 };
 
 static void register_types(void)
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [Qemu-devel] [PULL 40/41] chardev: qom-ify
  2017-01-27 13:45 [Qemu-devel] [PULL 00/41] Misc changes for 2017-01-27 Paolo Bonzini
                   ` (38 preceding siblings ...)
  2017-01-27 13:45 ` [Qemu-devel] [PULL 39/41] vc: " Paolo Bonzini
@ 2017-01-27 13:45 ` Paolo Bonzini
  2017-01-27 13:45 ` [Qemu-devel] [PULL 41/41] memory: don't sign-extend 32-bit writes Paolo Bonzini
  2017-01-27 16:58 ` [Qemu-devel] [PULL 00/41] Misc changes for 2017-01-27 Peter Maydell
  41 siblings, 0 replies; 43+ messages in thread
From: Paolo Bonzini @ 2017-01-27 13:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Turn Chardev into Object.

qemu_chr_alloc() is replaced by the qemu_chardev_new() constructor. It
will call qemu_char_open() to open/intialize the chardev with the
ChardevCommon *backend settings.

The CharDriver::create() callback is turned into a ChardevClass::open()
which is called from the newly introduced qemu_chardev_open().

"chardev-gdb" and "chardev-hci" are internal chardev and aren't
creatable directly with -chardev. Use a new internal flag to disable
them. We may want to use TYPE_USER_CREATABLE interface instead, or
perhaps allow -chardev usage.

Although in general we keep typename and macros private, unless the type
is being used by some other file, in this patch, all types and common
helper macros for qemu-char.c are in char.h. This is to help transition
now (some types must be declared early, while some aren't shared) and
when splitting in several units. This is to be improved later.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 backends/baum.c       |   66 +--
 backends/msmouse.c    |   57 ++-
 backends/testdev.c    |   34 +-
 gdbstub.c             |   39 +-
 hw/bt/hci-csr.c       |   55 +-
 include/sysemu/char.h |  107 ++--
 include/ui/console.h  |    2 +
 monitor.c             |    2 +-
 qemu-char.c           | 1327 ++++++++++++++++++++++++++-----------------------
 spice-qemu-char.c     |  142 +++---
 ui/console.c          |   62 ++-
 ui/gtk.c              |   51 +-
 vl.c                  |    2 +
 13 files changed, 1073 insertions(+), 873 deletions(-)

diff --git a/backends/baum.c b/backends/baum.c
index 8842936..0f418ed 100644
--- a/backends/baum.c
+++ b/backends/baum.c
@@ -100,6 +100,9 @@ typedef struct {
     QEMUTimer *cellCount_timer;
 } BaumChardev;
 
+#define TYPE_CHARDEV_BRAILLE "chardev-braille"
+#define BAUM_CHARDEV(obj) OBJECT_CHECK(BaumChardev, (obj), TYPE_CHARDEV_BRAILLE)
+
 /* Let's assume NABCC by default */
 enum way {
     DOTS2ASCII,
@@ -255,7 +258,7 @@ static int baum_deferred_init(BaumChardev *baum)
 /* The serial port can receive more of our data */
 static void baum_chr_accept_input(struct Chardev *chr)
 {
-    BaumChardev *baum = (BaumChardev *)chr;
+    BaumChardev *baum = BAUM_CHARDEV(chr);
     int room, first;
 
     if (!baum->out_buf_used)
@@ -281,7 +284,7 @@ static void baum_chr_accept_input(struct Chardev *chr)
 /* We want to send a packet */
 static void baum_write_packet(BaumChardev *baum, const uint8_t *buf, int len)
 {
-    Chardev *chr = (Chardev *)baum;
+    Chardev *chr = CHARDEV(baum);
     uint8_t io_buf[1 + 2 * len], *cur = io_buf;
     int room;
     *cur++ = ESC;
@@ -322,7 +325,7 @@ static void baum_write_packet(BaumChardev *baum, const uint8_t *buf, int len)
 /* Called when the other end seems to have a wrong idea of our display size */
 static void baum_cellCount_timer_cb(void *opaque)
 {
-    BaumChardev *baum = opaque;
+    BaumChardev *baum = BAUM_CHARDEV(opaque);
     uint8_t cell_count[] = { BAUM_RSP_CellCount, baum->x * baum->y };
     DPRINTF("Timeout waiting for DisplayData, sending cell count\n");
     baum_write_packet(baum, cell_count, sizeof(cell_count));
@@ -472,7 +475,7 @@ static int baum_eat_packet(BaumChardev *baum, const uint8_t *buf, int len)
 /* The other end is writing some data.  Store it and try to interpret */
 static int baum_chr_write(Chardev *chr, const uint8_t *buf, int len)
 {
-    BaumChardev *baum = (BaumChardev *)chr;
+    BaumChardev *baum = BAUM_CHARDEV(chr);
     int tocopy, cur, eaten, orig_len = len;
 
     if (!len)
@@ -529,7 +532,7 @@ static void baum_send_key2(BaumChardev *baum, uint8_t type, uint8_t value,
 /* We got some data on the BrlAPI socket */
 static void baum_chr_read(void *opaque)
 {
-    BaumChardev *baum = opaque;
+    BaumChardev *baum = BAUM_CHARDEV(opaque);
     brlapi_keyCode_t code;
     int ret;
     if (!baum->brlapi)
@@ -613,9 +616,9 @@ static void baum_chr_read(void *opaque)
     }
 }
 
-static void baum_chr_free(struct Chardev *chr)
+static void baum_chr_free(Chardev *chr)
 {
-    BaumChardev *baum = (BaumChardev *)chr;
+    BaumChardev *baum = BAUM_CHARDEV(chr);
 
     timer_free(baum->cellCount_timer);
     if (baum->brlapi) {
@@ -624,24 +627,14 @@ static void baum_chr_free(struct Chardev *chr)
     }
 }
 
-static Chardev *baum_chr_init(const CharDriver *driver,
-                              const char *id,
-                              ChardevBackend *backend,
-                              ChardevReturn *ret,
-                              bool *be_opened,
-                              Error **errp)
+static void baum_chr_open(Chardev *chr,
+                          ChardevBackend *backend,
+                          bool *be_opened,
+                          Error **errp)
 {
-    ChardevCommon *common = backend->u.braille.data;
-    BaumChardev *baum;
-    Chardev *chr;
+    BaumChardev *baum = BAUM_CHARDEV(chr);
     brlapi_handle_t *handle;
 
-    chr = qemu_chr_alloc(driver, common, errp);
-    if (!chr) {
-        return NULL;
-    }
-    baum = (BaumChardev *)chr;
-
     handle = g_malloc0(brlapi_getHandleSize());
     baum->brlapi = handle;
 
@@ -649,34 +642,41 @@ static Chardev *baum_chr_init(const CharDriver *driver,
     if (baum->brlapi_fd == -1) {
         error_setg(errp, "brlapi__openConnection: %s",
                    brlapi_strerror(brlapi_error_location()));
-        goto fail_handle;
+        g_free(handle);
+        return;
     }
     baum->deferred_init = 0;
 
     baum->cellCount_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, baum_cellCount_timer_cb, baum);
 
     qemu_set_fd_handler(baum->brlapi_fd, baum_chr_read, NULL, baum);
+}
 
-    return chr;
+static void char_braille_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
 
-fail_handle:
-    g_free(handle);
-    g_free(chr);
-    return NULL;
+    cc->open = baum_chr_open;
+    cc->chr_write = baum_chr_write;
+    cc->chr_accept_input = baum_chr_accept_input;
+    cc->chr_free = baum_chr_free;
 }
 
+static const TypeInfo char_braille_type_info = {
+    .name = TYPE_CHARDEV_BRAILLE,
+    .parent = TYPE_CHARDEV,
+    .instance_size = sizeof(BaumChardev),
+    .class_init = char_braille_class_init,
+};
+
 static void register_types(void)
 {
     static const CharDriver driver = {
-        .instance_size = sizeof(BaumChardev),
         .kind = CHARDEV_BACKEND_KIND_BRAILLE,
-        .create = baum_chr_init,
-        .chr_write = baum_chr_write,
-        .chr_accept_input = baum_chr_accept_input,
-        .chr_free = baum_chr_free,
     };
 
     register_char_driver(&driver);
+    type_register_static(&char_braille_type_info);
 }
 
 type_init(register_types);
diff --git a/backends/msmouse.c b/backends/msmouse.c
index 4e474da..936a547 100644
--- a/backends/msmouse.c
+++ b/backends/msmouse.c
@@ -41,9 +41,13 @@ typedef struct {
     int outlen;
 } MouseChardev;
 
+#define TYPE_CHARDEV_MSMOUSE "chardev-msmouse"
+#define MOUSE_CHARDEV(obj)                                      \
+    OBJECT_CHECK(MouseChardev, (obj), TYPE_CHARDEV_MSMOUSE)
+
 static void msmouse_chr_accept_input(Chardev *chr)
 {
-    MouseChardev *mouse = (MouseChardev *)chr;
+    MouseChardev *mouse = MOUSE_CHARDEV(chr);
     int len;
 
     len = qemu_chr_be_can_write(chr);
@@ -98,7 +102,7 @@ static void msmouse_queue_event(MouseChardev *mouse)
 static void msmouse_input_event(DeviceState *dev, QemuConsole *src,
                                 InputEvent *evt)
 {
-    MouseChardev *mouse = (MouseChardev *)dev;
+    MouseChardev *mouse = MOUSE_CHARDEV(dev);
     InputMoveEvent *move;
     InputBtnEvent *btn;
 
@@ -122,8 +126,8 @@ static void msmouse_input_event(DeviceState *dev, QemuConsole *src,
 
 static void msmouse_input_sync(DeviceState *dev)
 {
-    MouseChardev *mouse = (MouseChardev *)dev;
-    Chardev *chr = (Chardev *)dev;
+    MouseChardev *mouse = MOUSE_CHARDEV(dev);
+    Chardev *chr = CHARDEV(dev);
 
     msmouse_queue_event(mouse);
     msmouse_chr_accept_input(chr);
@@ -137,7 +141,7 @@ static int msmouse_chr_write(struct Chardev *s, const uint8_t *buf, int len)
 
 static void msmouse_chr_free(struct Chardev *chr)
 {
-    MouseChardev *mouse = (MouseChardev *)chr;
+    MouseChardev *mouse = MOUSE_CHARDEV(chr);
 
     qemu_input_handler_unregister(mouse->hs);
 }
@@ -149,42 +153,43 @@ static QemuInputHandler msmouse_handler = {
     .sync  = msmouse_input_sync,
 };
 
-static Chardev *qemu_chr_open_msmouse(const CharDriver *driver,
-                                      const char *id,
-                                      ChardevBackend *backend,
-                                      ChardevReturn *ret,
-                                      bool *be_opened,
-                                      Error **errp)
+static void msmouse_chr_open(Chardev *chr,
+                             ChardevBackend *backend,
+                             bool *be_opened,
+                             Error **errp)
 {
-    ChardevCommon *common = backend->u.msmouse.data;
-    MouseChardev *mouse;
-    Chardev *chr;
+    MouseChardev *mouse = MOUSE_CHARDEV(chr);
 
-    chr = qemu_chr_alloc(driver, common, errp);
-    if (!chr) {
-        return NULL;
-    }
     *be_opened = false;
-
-    mouse = (MouseChardev *)chr;
     mouse->hs = qemu_input_handler_register((DeviceState *)mouse,
                                             &msmouse_handler);
+}
 
+static void char_msmouse_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
 
-    return chr;
+    cc->open = msmouse_chr_open;
+    cc->chr_write = msmouse_chr_write;
+    cc->chr_accept_input = msmouse_chr_accept_input;
+    cc->chr_free = msmouse_chr_free;
 }
 
+static const TypeInfo char_msmouse_type_info = {
+    .name = TYPE_CHARDEV_MSMOUSE,
+    .parent = TYPE_CHARDEV,
+    .instance_size = sizeof(MouseChardev),
+    .class_init = char_msmouse_class_init,
+};
+
 static void register_types(void)
 {
     static const CharDriver driver = {
-        .instance_size = sizeof(MouseChardev),
         .kind = CHARDEV_BACKEND_KIND_MSMOUSE,
-        .create = qemu_chr_open_msmouse,
-        .chr_write = msmouse_chr_write,
-        .chr_accept_input = msmouse_chr_accept_input,
-        .chr_free = msmouse_chr_free,
     };
+
     register_char_driver(&driver);
+    type_register_static(&char_msmouse_type_info);
 }
 
 type_init(register_types);
diff --git a/backends/testdev.c b/backends/testdev.c
index 268b380..ea15143 100644
--- a/backends/testdev.c
+++ b/backends/testdev.c
@@ -36,6 +36,10 @@ typedef struct {
     int in_buf_used;
 } TestdevChardev;
 
+#define TYPE_CHARDEV_TESTDEV "chardev-testdev"
+#define TESTDEV_CHARDEV(obj)                                    \
+    OBJECT_CHECK(TestdevChardev, (obj), TYPE_CHARDEV_TESTDEV)
+
 /* Try to interpret a whole incoming packet */
 static int testdev_eat_packet(TestdevChardev *testdev)
 {
@@ -78,9 +82,9 @@ static int testdev_eat_packet(TestdevChardev *testdev)
 }
 
 /* The other end is writing some data.  Store it and try to interpret */
-static int testdev_write(Chardev *chr, const uint8_t *buf, int len)
+static int testdev_chr_write(Chardev *chr, const uint8_t *buf, int len)
 {
-    TestdevChardev *testdev = (TestdevChardev *)chr;
+    TestdevChardev *testdev = TESTDEV_CHARDEV(chr);
     int tocopy, eaten, orig_len = len;
 
     while (len) {
@@ -103,30 +107,28 @@ static int testdev_write(Chardev *chr, const uint8_t *buf, int len)
     return orig_len;
 }
 
-static Chardev *chr_testdev_init(const CharDriver *driver,
-                                 const char *id,
-                                 ChardevBackend *backend,
-                                 ChardevReturn *ret,
-                                 bool *be_opened,
-                                 Error **errp)
+static void char_testdev_class_init(ObjectClass *oc, void *data)
 {
-    TestdevChardev *testdev = g_new0(TestdevChardev, 1);;
-    Chardev *chr = (Chardev *)testdev;
-
-    chr->driver = driver;
+    ChardevClass *cc = CHARDEV_CLASS(oc);
 
-    return chr;
+    cc->chr_write = testdev_chr_write;
 }
 
+static const TypeInfo char_testdev_type_info = {
+    .name = TYPE_CHARDEV_TESTDEV,
+    .parent = TYPE_CHARDEV,
+    .instance_size = sizeof(TestdevChardev),
+    .class_init = char_testdev_class_init,
+};
+
 static void register_types(void)
 {
     static const CharDriver driver = {
-        .instance_size = sizeof(TestdevChardev),
         .kind = CHARDEV_BACKEND_KIND_TESTDEV,
-        .create = chr_testdev_init,
-        .chr_write = testdev_write,
     };
+
     register_char_driver(&driver);
+    type_register_static(&char_testdev_type_info);
 }
 
 type_init(register_types);
diff --git a/gdbstub.c b/gdbstub.c
index 396f892..1d5d2e2 100644
--- a/gdbstub.c
+++ b/gdbstub.c
@@ -1724,18 +1724,35 @@ static void gdb_sigterm_handler(int signal)
 }
 #endif
 
+static void gdb_monitor_open(Chardev *chr, ChardevBackend *backend,
+                             bool *be_opened, Error **errp)
+{
+    *be_opened = false;
+}
+
+static void char_gdb_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->internal = true;
+    cc->open = gdb_monitor_open;
+    cc->chr_write = gdb_monitor_write;
+}
+
+#define TYPE_CHARDEV_GDB "chardev-gdb"
+
+static const TypeInfo char_gdb_type_info = {
+    .name = TYPE_CHARDEV_GDB,
+    .parent = TYPE_CHARDEV,
+    .class_init = char_gdb_class_init,
+};
+
 int gdbserver_start(const char *device)
 {
     GDBState *s;
     char gdbstub_device_name[128];
     Chardev *chr = NULL;
     Chardev *mon_chr;
-    ChardevCommon common = { 0 };
-    static const CharDriver driver = {
-        .instance_size = sizeof(Chardev),
-        .kind = -1,
-        .chr_write = gdb_monitor_write,
-    };
 
     if (!device)
         return -1;
@@ -1768,7 +1785,8 @@ int gdbserver_start(const char *device)
         qemu_add_vm_change_state_handler(gdb_vm_state_change, NULL);
 
         /* Initialize a monitor terminal for gdb */
-        mon_chr = qemu_chr_alloc(&driver, &common, &error_abort);
+        mon_chr = qemu_chardev_new(NULL, TYPE_CHARDEV_GDB,
+                                   NULL, &error_abort);
         monitor_init(mon_chr, 0);
     } else {
         if (qemu_chr_fe_get_driver(&s->chr)) {
@@ -1791,4 +1809,11 @@ int gdbserver_start(const char *device)
 
     return 0;
 }
+
+static void register_types(void)
+{
+    type_register_static(&char_gdb_type_info);
+}
+
+type_init(register_types);
 #endif
diff --git a/hw/bt/hci-csr.c b/hw/bt/hci-csr.c
index 153ed2f..3c19384 100644
--- a/hw/bt/hci-csr.c
+++ b/hw/bt/hci-csr.c
@@ -55,6 +55,9 @@ struct csrhci_s {
     struct HCIInfo *hci;
 };
 
+#define TYPE_CHARDEV_HCI "chardev-hci"
+#define HCI_CHARDEV(obj) OBJECT_CHECK(struct csrhci_s, (obj), TYPE_CHARDEV_HCI)
+
 /* H4+ packet types */
 enum {
     H4_CMD_PKT   = 1,
@@ -462,23 +465,12 @@ qemu_irq *csrhci_pins_get(Chardev *chr)
     return s->pins;
 }
 
-Chardev *uart_hci_init(void)
+static void csrhci_open(Chardev *chr,
+                        ChardevBackend *backend,
+                        bool *be_opened,
+                        Error **errp)
 {
-    static const CharDriver hci_driver = {
-        .instance_size = sizeof(struct csrhci_s),
-        .kind = -1,
-        .chr_write = csrhci_write,
-        .chr_ioctl = csrhci_ioctl,
-    };
-    Error *err = NULL;
-    ChardevCommon common = { 0, };
-    Chardev *chr = qemu_chr_alloc(&hci_driver, &common, &err);
-    struct csrhci_s *s = (struct csrhci_s *)chr;
-
-    if (err) {
-        error_report_err(err);
-        return NULL;
-    }
+    struct csrhci_s *s = HCI_CHARDEV(chr);
 
     s->hci = qemu_next_hci();
     s->hci->opaque = s;
@@ -488,6 +480,35 @@ Chardev *uart_hci_init(void)
     s->out_tm = timer_new_ns(QEMU_CLOCK_VIRTUAL, csrhci_out_tick, s);
     s->pins = qemu_allocate_irqs(csrhci_pins, s, __csrhci_pins);
     csrhci_reset(s);
+    *be_opened = false;
+}
+
+static void char_hci_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->internal = true;
+    cc->open = csrhci_open;
+    cc->chr_write = csrhci_write;
+    cc->chr_ioctl = csrhci_ioctl;
+}
+
+static const TypeInfo char_hci_type_info = {
+    .name = TYPE_CHARDEV_HCI,
+    .parent = TYPE_CHARDEV,
+    .instance_size = sizeof(struct csrhci_s),
+    .class_init = char_hci_class_init,
+};
 
-    return chr;
+Chardev *uart_hci_init(void)
+{
+    return qemu_chardev_new(NULL, TYPE_CHARDEV_HCI,
+                            NULL, &error_abort);
 }
+
+static void register_types(void)
+{
+    type_register_static(&char_hci_type_info);
+}
+
+type_init(register_types);
diff --git a/include/sysemu/char.h b/include/sysemu/char.h
index baa9e8f..da0e7dd 100644
--- a/include/sysemu/char.h
+++ b/include/sysemu/char.h
@@ -10,6 +10,7 @@
 #include "qapi/qmp/qstring.h"
 #include "qemu/main-loop.h"
 #include "qemu/bitmap.h"
+#include "qom/object.h"
 
 /* character device */
 
@@ -90,7 +91,8 @@ typedef struct CharBackend {
 typedef struct CharDriver CharDriver;
 
 struct Chardev {
-    const CharDriver *driver;
+    Object parent_obj;
+
     QemuMutex chr_write_lock;
     CharBackend *be;
     char *label;
@@ -103,18 +105,6 @@ struct Chardev {
 };
 
 /**
- * qemu_chr_alloc:
- * @backend: the common backend config
- * @errp: pointer to a NULL-initialized error object
- *
- * Allocate and initialize a new Chardev.
- *
- * Returns: a newly allocated Chardev, or NULL on error.
- */
-Chardev *qemu_chr_alloc(const CharDriver *driver,
-                        ChardevCommon *backend, Error **errp);
-
-/**
  * @qemu_chr_new_from_opts:
  *
  * Create a new character backend from a QemuOpts list.
@@ -455,54 +445,73 @@ void qemu_chr_fe_accept_input(CharBackend *be);
 int qemu_chr_add_client(Chardev *s, int fd);
 Chardev *qemu_chr_find(const char *name);
 
-/**
- * @qemu_chr_get_kind:
- *
- * Returns the kind of char backend, or -1 if unspecified.
- */
-ChardevBackendKind qemu_chr_get_kind(const Chardev *chr);
-
-static inline bool qemu_chr_is_ringbuf(const Chardev *chr)
-{
-    return qemu_chr_get_kind(chr) == CHARDEV_BACKEND_KIND_RINGBUF ||
-        qemu_chr_get_kind(chr) == CHARDEV_BACKEND_KIND_MEMORY;
-}
-
 bool qemu_chr_has_feature(Chardev *chr,
                           CharDriverFeature feature);
 void qemu_chr_set_feature(Chardev *chr,
                           CharDriverFeature feature);
 QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename);
 
+#define TYPE_CHARDEV "chardev"
+#define CHARDEV(obj) OBJECT_CHECK(Chardev, (obj), TYPE_CHARDEV)
+#define CHARDEV_CLASS(klass) \
+    OBJECT_CLASS_CHECK(ChardevClass, (klass), TYPE_CHARDEV)
+#define CHARDEV_GET_CLASS(obj) \
+    OBJECT_GET_CLASS(ChardevClass, (obj), TYPE_CHARDEV)
+
+#define TYPE_CHARDEV_NULL "chardev-null"
+#define TYPE_CHARDEV_MUX "chardev-mux"
+#define TYPE_CHARDEV_RINGBUF "chardev-ringbuf"
+#define TYPE_CHARDEV_PTY "chardev-pty"
+#define TYPE_CHARDEV_CONSOLE "chardev-console"
+#define TYPE_CHARDEV_STDIO "chardev-stdio"
+#define TYPE_CHARDEV_PIPE "chardev-pipe"
+#define TYPE_CHARDEV_MEMORY "chardev-memory"
+#define TYPE_CHARDEV_PARALLEL "chardev-parallel"
+#define TYPE_CHARDEV_FILE "chardev-file"
+#define TYPE_CHARDEV_SERIAL "chardev-serial"
+#define TYPE_CHARDEV_SOCKET "chardev-socket"
+#define TYPE_CHARDEV_UDP "chardev-udp"
+
+#define CHARDEV_IS_MUX(chr) \
+    object_dynamic_cast(OBJECT(chr), TYPE_CHARDEV_MUX)
+#define CHARDEV_IS_RINGBUF(chr) \
+    object_dynamic_cast(OBJECT(chr), TYPE_CHARDEV_RINGBUF)
+#define CHARDEV_IS_PTY(chr) \
+    object_dynamic_cast(OBJECT(chr), TYPE_CHARDEV_PTY)
+
+typedef struct ChardevClass {
+    ObjectClass parent_class;
+
+    bool internal; /* TODO: eventually use TYPE_USER_CREATABLE */
+
+    void (*open)(Chardev *chr, ChardevBackend *backend,
+                 bool *be_opened, Error **errp);
+
+    int (*chr_write)(Chardev *s, const uint8_t *buf, int len);
+    int (*chr_sync_read)(Chardev *s, const uint8_t *buf, int len);
+    GSource *(*chr_add_watch)(Chardev *s, GIOCondition cond);
+    void (*chr_update_read_handler)(Chardev *s, GMainContext *context);
+    int (*chr_ioctl)(Chardev *s, int cmd, void *arg);
+    int (*get_msgfds)(Chardev *s, int* fds, int num);
+    int (*set_msgfds)(Chardev *s, int *fds, int num);
+    int (*chr_add_client)(Chardev *chr, int fd);
+    int (*chr_wait_connected)(Chardev *chr, Error **errp);
+    void (*chr_free)(Chardev *chr);
+    void (*chr_disconnect)(Chardev *chr);
+    void (*chr_accept_input)(Chardev *chr);
+    void (*chr_set_echo)(Chardev *chr, bool echo);
+    void (*chr_set_fe_open)(Chardev *chr, int fe_open);
+} ChardevClass;
+
 struct CharDriver {
     ChardevBackendKind kind;
     const char *alias;
     void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp);
-    Chardev *(*create)(const CharDriver *driver,
-                       const char *id,
-                       ChardevBackend *backend,
-                       ChardevReturn *ret, bool *be_opened,
-                       Error **errp);
-    size_t instance_size;
-
-    int (*chr_write)(struct Chardev *s, const uint8_t *buf, int len);
-    int (*chr_sync_read)(struct Chardev *s,
-                         const uint8_t *buf, int len);
-    GSource *(*chr_add_watch)(struct Chardev *s, GIOCondition cond);
-    void (*chr_update_read_handler)(struct Chardev *s,
-                                    GMainContext *context);
-    int (*chr_ioctl)(struct Chardev *s, int cmd, void *arg);
-    int (*get_msgfds)(struct Chardev *s, int* fds, int num);
-    int (*set_msgfds)(struct Chardev *s, int *fds, int num);
-    int (*chr_add_client)(struct Chardev *chr, int fd);
-    int (*chr_wait_connected)(struct Chardev *chr, Error **errp);
-    void (*chr_free)(struct Chardev *chr);
-    void (*chr_disconnect)(struct Chardev *chr);
-    void (*chr_accept_input)(struct Chardev *chr);
-    void (*chr_set_echo)(struct Chardev *chr, bool echo);
-    void (*chr_set_fe_open)(struct Chardev *chr, int fe_open);
 };
 
+Chardev *qemu_chardev_new(const char *id, const char *typename,
+                          ChardevBackend *backend, Error **errp);
+
 void register_char_driver(const CharDriver *driver);
 
 extern int term_escape_char;
diff --git a/include/ui/console.h b/include/ui/console.h
index b59e7b8..af6350e 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -383,6 +383,8 @@ void graphic_hw_invalidate(QemuConsole *con);
 void graphic_hw_text_update(QemuConsole *con, console_ch_t *chardata);
 void graphic_hw_gl_block(QemuConsole *con, bool block);
 
+void qemu_console_early_init(void);
+
 QemuConsole *qemu_console_lookup_by_index(unsigned int index);
 QemuConsole *qemu_console_lookup_by_device(DeviceState *dev, uint32_t head);
 QemuConsole *qemu_console_lookup_by_device_name(const char *device_id,
diff --git a/monitor.c b/monitor.c
index 065f659..25a480a 100644
--- a/monitor.c
+++ b/monitor.c
@@ -3196,7 +3196,7 @@ static void ringbuf_completion(ReadLineState *rs, const char *str)
 
         if (!strncmp(chr_info->label, str, len)) {
             Chardev *chr = qemu_chr_find(chr_info->label);
-            if (chr && qemu_chr_is_ringbuf(chr)) {
+            if (chr && CHARDEV_IS_RINGBUF(chr)) {
                 readline_add_completion(rs, chr_info->label);
             }
         }
diff --git a/qemu-char.c b/qemu-char.c
index 113c4a8..6b4a299 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -160,43 +160,6 @@ static char *sockaddr_to_str(struct sockaddr_storage *ss, socklen_t ss_len,
 static QTAILQ_HEAD(ChardevHead, Chardev) chardevs =
     QTAILQ_HEAD_INITIALIZER(chardevs);
 
-static void qemu_chr_free_common(Chardev *chr);
-
-Chardev *qemu_chr_alloc(const CharDriver *driver,
-                                ChardevCommon *backend, Error **errp)
-{
-    Chardev *chr;
-
-    assert(driver);
-    assert(driver->chr_write);
-    assert(driver->instance_size >= sizeof(Chardev));
-
-    chr = g_malloc0(driver->instance_size);
-    qemu_mutex_init(&chr->chr_write_lock);
-    if (backend->has_logfile) {
-        int flags = O_WRONLY | O_CREAT;
-        if (backend->has_logappend &&
-            backend->logappend) {
-            flags |= O_APPEND;
-        } else {
-            flags |= O_TRUNC;
-        }
-        chr->logfd = qemu_open(backend->logfile, flags, 0666);
-        if (chr->logfd < 0) {
-            error_setg_errno(errp, errno,
-                             "Unable to open logfile %s",
-                             backend->logfile);
-            g_free(chr);
-            return NULL;
-        }
-    } else {
-        chr->logfd = -1;
-    }
-    chr->driver = driver;
-
-    return chr;
-}
-
 void qemu_chr_be_event(Chardev *s, int event)
 {
     CharBackend *be = s->be;
@@ -254,13 +217,14 @@ static void qemu_chr_fe_write_log(Chardev *s,
 static int qemu_chr_fe_write_buffer(Chardev *s,
                                     const uint8_t *buf, int len, int *offset)
 {
+    ChardevClass *cc = CHARDEV_GET_CLASS(s);
     int res = 0;
     *offset = 0;
 
     qemu_mutex_lock(&s->chr_write_lock);
     while (*offset < len) {
     retry:
-        res = s->driver->chr_write(s, buf + *offset, len - *offset);
+        res = cc->chr_write(s, buf + *offset, len - *offset);
         if (res < 0 && errno == EAGAIN) {
             g_usleep(100);
             goto retry;
@@ -288,6 +252,7 @@ static bool qemu_chr_replay(Chardev *chr)
 int qemu_chr_fe_write(CharBackend *be, const uint8_t *buf, int len)
 {
     Chardev *s = be->chr;
+    ChardevClass *cc;
     int ret;
 
     if (!s) {
@@ -302,8 +267,9 @@ int qemu_chr_fe_write(CharBackend *be, const uint8_t *buf, int len)
         return ret;
     }
 
+    cc = CHARDEV_GET_CLASS(s);
     qemu_mutex_lock(&s->chr_write_lock);
-    ret = s->driver->chr_write(s, buf, len);
+    ret = cc->chr_write(s, buf, len);
 
     if (ret > 0) {
         qemu_chr_fe_write_log(s, buf, ret);
@@ -359,7 +325,7 @@ int qemu_chr_fe_read_all(CharBackend *be, uint8_t *buf, int len)
     int offset = 0, counter = 10;
     int res;
 
-    if (!s || !s->driver->chr_sync_read) {
+    if (!s || !CHARDEV_GET_CLASS(s)->chr_sync_read) {
         return 0;
     }
 
@@ -369,7 +335,8 @@ int qemu_chr_fe_read_all(CharBackend *be, uint8_t *buf, int len)
 
     while (offset < len) {
     retry:
-        res = s->driver->chr_sync_read(s, buf + offset, len - offset);
+        res = CHARDEV_GET_CLASS(s)->chr_sync_read(s, buf + offset,
+                                                  len - offset);
         if (res == -1 && errno == EAGAIN) {
             g_usleep(100);
             goto retry;
@@ -404,10 +371,10 @@ int qemu_chr_fe_ioctl(CharBackend *be, int cmd, void *arg)
     Chardev *s = be->chr;
     int res;
 
-    if (!s || !s->driver->chr_ioctl || qemu_chr_replay(s)) {
+    if (!s || !CHARDEV_GET_CLASS(s)->chr_ioctl || qemu_chr_replay(s)) {
         res = -ENOTSUP;
     } else {
-        res = s->driver->chr_ioctl(s, cmd, arg);
+        res = CHARDEV_GET_CLASS(s)->chr_ioctl(s, cmd, arg);
     }
 
     return res;
@@ -466,7 +433,8 @@ int qemu_chr_fe_get_msgfds(CharBackend *be, int *fds, int len)
         return -1;
     }
 
-    return s->driver->get_msgfds ? s->driver->get_msgfds(s, fds, len) : -1;
+    return CHARDEV_GET_CLASS(s)->get_msgfds ?
+        CHARDEV_GET_CLASS(s)->get_msgfds(s, fds, len) : -1;
 }
 
 int qemu_chr_fe_set_msgfds(CharBackend *be, int *fds, int num)
@@ -477,12 +445,14 @@ int qemu_chr_fe_set_msgfds(CharBackend *be, int *fds, int num)
         return -1;
     }
 
-    return s->driver->set_msgfds ? s->driver->set_msgfds(s, fds, num) : -1;
+    return CHARDEV_GET_CLASS(s)->set_msgfds ?
+        CHARDEV_GET_CLASS(s)->set_msgfds(s, fds, num) : -1;
 }
 
 int qemu_chr_add_client(Chardev *s, int fd)
 {
-    return s->driver->chr_add_client ? s->driver->chr_add_client(s, fd) : -1;
+    return CHARDEV_GET_CLASS(s)->chr_add_client ?
+        CHARDEV_GET_CLASS(s)->chr_add_client(s, fd) : -1;
 }
 
 void qemu_chr_fe_accept_input(CharBackend *be)
@@ -493,8 +463,8 @@ void qemu_chr_fe_accept_input(CharBackend *be)
         return;
     }
 
-    if (s->driver->chr_accept_input) {
-        s->driver->chr_accept_input(s);
+    if (CHARDEV_GET_CLASS(s)->chr_accept_input) {
+        CHARDEV_GET_CLASS(s)->chr_accept_input(s);
     }
     qemu_notify_event();
 }
@@ -515,34 +485,98 @@ static void remove_fd_in_watch(Chardev *chr);
 static void mux_chr_set_handlers(Chardev *chr, GMainContext *context);
 static void mux_set_focus(Chardev *chr, int focus);
 
-static int null_chr_write(Chardev *chr, const uint8_t *buf, int len)
+static void qemu_char_open(Chardev *chr, ChardevBackend *backend,
+                           bool *be_opened, Error **errp)
 {
-    return len;
+    ChardevClass *cc = CHARDEV_GET_CLASS(chr);
+    /* Any ChardevCommon member would work */
+    ChardevCommon *common = backend ? backend->u.null.data : NULL;
+
+    if (common && common->has_logfile) {
+        int flags = O_WRONLY | O_CREAT;
+        if (common->has_logappend &&
+            common->logappend) {
+            flags |= O_APPEND;
+        } else {
+            flags |= O_TRUNC;
+        }
+        chr->logfd = qemu_open(common->logfile, flags, 0666);
+        if (chr->logfd < 0) {
+            error_setg_errno(errp, errno,
+                             "Unable to open logfile %s",
+                             common->logfile);
+            return;
+        }
+    }
+
+    if (cc->open) {
+        cc->open(chr, backend, be_opened, errp);
+    }
 }
 
-static Chardev *qemu_chr_open_null(const CharDriver *driver,
-                                   const char *id,
-                                   ChardevBackend *backend,
-                                   ChardevReturn *ret,
-                                   bool *be_opened,
-                                   Error **errp)
+static void char_init(Object *obj)
 {
-    Chardev *chr;
-    ChardevCommon *common = backend->u.null.data;
+    Chardev *chr = CHARDEV(obj);
 
-    chr = qemu_chr_alloc(driver, common, errp);
-    if (!chr) {
-        return NULL;
+    chr->logfd = -1;
+    qemu_mutex_init(&chr->chr_write_lock);
+}
+
+static void char_finalize(Object *obj)
+{
+    Chardev *chr = CHARDEV(obj);
+
+    if (chr->be) {
+        chr->be->chr = NULL;
+    }
+    g_free(chr->filename);
+    g_free(chr->label);
+    if (chr->logfd != -1) {
+        close(chr->logfd);
     }
+    qemu_mutex_destroy(&chr->chr_write_lock);
+}
+
+static const TypeInfo char_type_info = {
+    .name = TYPE_CHARDEV,
+    .parent = TYPE_OBJECT,
+    .instance_size = sizeof(Chardev),
+    .instance_init = char_init,
+    .instance_finalize = char_finalize,
+    .abstract = true,
+    .class_size = sizeof(ChardevClass),
+};
+
+static int null_chr_write(Chardev *chr, const uint8_t *buf, int len)
+{
+    return len;
+}
+
+static void null_chr_open(Chardev *chr,
+                          ChardevBackend *backend,
+                          bool *be_opened,
+                          Error **errp)
+{
     *be_opened = false;
-    return chr;
 }
 
 static const CharDriver null_driver = {
-    .instance_size = sizeof(Chardev),
     .kind = CHARDEV_BACKEND_KIND_NULL,
-    .create = qemu_chr_open_null,
-    .chr_write = null_chr_write,
+};
+
+static void char_null_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->open = null_chr_open;
+    cc->chr_write = null_chr_write;
+}
+
+static const TypeInfo char_null_type_info = {
+    .name = TYPE_CHARDEV_NULL,
+    .parent = TYPE_CHARDEV,
+    .instance_size = sizeof(Chardev),
+    .class_init = char_null_class_init,
 };
 
 /* MUX driver for serial I/O splitting */
@@ -570,10 +604,12 @@ struct MuxChardev {
     int64_t timestamps_start;
 };
 
+#define MUX_CHARDEV(obj) OBJECT_CHECK(MuxChardev, (obj), TYPE_CHARDEV_MUX)
+
 /* Called with chr_write_lock held.  */
 static int mux_chr_write(Chardev *chr, const uint8_t *buf, int len)
 {
-    MuxChardev *d = (MuxChardev *)chr;
+    MuxChardev *d = MUX_CHARDEV(chr);
     int ret;
     if (!d->timestamps) {
         ret = qemu_chr_fe_write(&d->chr, buf, len);
@@ -707,7 +743,7 @@ static int mux_proc_byte(Chardev *chr, MuxChardev *d, int ch)
 
 static void mux_chr_accept_input(Chardev *chr)
 {
-    MuxChardev *d = (MuxChardev *)chr;
+    MuxChardev *d = MUX_CHARDEV(chr);
     int m = d->focus;
     CharBackend *be = d->backends[m];
 
@@ -720,7 +756,7 @@ static void mux_chr_accept_input(Chardev *chr)
 
 static int mux_chr_can_read(void *opaque)
 {
-    MuxChardev *d = opaque;
+    MuxChardev *d = MUX_CHARDEV(opaque);
     int m = d->focus;
     CharBackend *be = d->backends[m];
 
@@ -737,8 +773,8 @@ static int mux_chr_can_read(void *opaque)
 
 static void mux_chr_read(void *opaque, const uint8_t *buf, int size)
 {
-    Chardev *chr = opaque;
-    MuxChardev *d = opaque;
+    Chardev *chr = CHARDEV(opaque);
+    MuxChardev *d = MUX_CHARDEV(opaque);
     int m = d->focus;
     CharBackend *be = d->backends[m];
     int i;
@@ -760,7 +796,7 @@ static bool muxes_realized;
 
 static void mux_chr_event(void *opaque, int event)
 {
-    MuxChardev *d = opaque;
+    MuxChardev *d = MUX_CHARDEV(opaque);
     int i;
 
     if (!muxes_realized) {
@@ -789,8 +825,8 @@ static void muxes_realize_done(Notifier *notifier, void *unused)
     Chardev *chr;
 
     QTAILQ_FOREACH(chr, &chardevs, next) {
-        if (qemu_chr_get_kind(chr) == CHARDEV_BACKEND_KIND_MUX) {
-            MuxChardev *d = (MuxChardev *)chr;
+        if (CHARDEV_IS_MUX(chr)) {
+            MuxChardev *d = MUX_CHARDEV(chr);
             int i;
 
             /* send OPENED to all already-attached FEs */
@@ -812,19 +848,20 @@ static Notifier muxes_realize_notify = {
 
 static GSource *mux_chr_add_watch(Chardev *s, GIOCondition cond)
 {
-    MuxChardev *d = (MuxChardev *)s;
+    MuxChardev *d = MUX_CHARDEV(s);
     Chardev *chr = qemu_chr_fe_get_driver(&d->chr);
+    ChardevClass *cc = CHARDEV_GET_CLASS(chr);
 
-    if (!chr->driver->chr_add_watch) {
+    if (!cc->chr_add_watch) {
         return NULL;
     }
 
-    return chr->driver->chr_add_watch(chr, cond);
+    return cc->chr_add_watch(chr, cond);
 }
 
 static void mux_chr_free(struct Chardev *chr)
 {
-    MuxChardev *d = (MuxChardev *)chr;
+    MuxChardev *d = MUX_CHARDEV(chr);
     int i;
 
     for (i = 0; i < d->mux_cnt; i++) {
@@ -838,7 +875,7 @@ static void mux_chr_free(struct Chardev *chr)
 
 static void mux_chr_set_handlers(Chardev *chr, GMainContext *context)
 {
-    MuxChardev *d = (MuxChardev *)chr;
+    MuxChardev *d = MUX_CHARDEV(chr);
 
     /* Fix up the real driver with mux routines */
     qemu_chr_fe_set_handlers(&d->chr,
@@ -851,7 +888,7 @@ static void mux_chr_set_handlers(Chardev *chr, GMainContext *context)
 
 static void mux_set_focus(Chardev *chr, int focus)
 {
-    MuxChardev *d = (MuxChardev *)chr;
+    MuxChardev *d = MUX_CHARDEV(chr);
 
     assert(focus >= 0);
     assert(focus < d->mux_cnt);
@@ -865,40 +902,27 @@ static void mux_set_focus(Chardev *chr, int focus)
     mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_IN);
 }
 
-static Chardev *qemu_chr_open_mux(const CharDriver *driver,
-                                  const char *id,
-                                  ChardevBackend *backend,
-                                  ChardevReturn *ret,
-                                  bool *be_opened,
-                                  Error **errp)
+static void qemu_chr_open_mux(Chardev *chr,
+                              ChardevBackend *backend,
+                              bool *be_opened,
+                              Error **errp)
 {
     ChardevMux *mux = backend->u.mux.data;
-    Chardev *chr, *drv;
-    MuxChardev *d;
-    ChardevCommon *common = qapi_ChardevMux_base(mux);
+    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 NULL;
+        return;
     }
 
-    chr = qemu_chr_alloc(driver, common, errp);
-    if (!chr) {
-        return NULL;
-    }
-    d = (MuxChardev *)chr;
     d->focus = -1;
     /* only default to opened state if we've realized the initial
      * set of muxes
      */
     *be_opened = muxes_realized;
-    if (!qemu_chr_fe_init(&d->chr, drv, errp)) {
-        qemu_chr_free(chr);
-        return NULL;
-    }
-
-    return chr;
+    qemu_chr_fe_init(&d->chr, drv, errp);
 }
 
 Chardev *qemu_chr_fe_get_driver(CharBackend *be)
@@ -910,8 +934,8 @@ bool qemu_chr_fe_init(CharBackend *b, Chardev *s, Error **errp)
 {
     int tag = 0;
 
-    if (qemu_chr_get_kind(s) == CHARDEV_BACKEND_KIND_MUX) {
-        MuxChardev *d = (MuxChardev *)s;
+    if (CHARDEV_IS_MUX(s)) {
+        MuxChardev *d = MUX_CHARDEV(s);
 
         if (d->mux_cnt >= MAX_MUX) {
             goto unavailable;
@@ -937,8 +961,8 @@ unavailable:
 
 static bool qemu_chr_is_busy(Chardev *s)
 {
-    if (qemu_chr_get_kind(s) == CHARDEV_BACKEND_KIND_MUX) {
-        MuxChardev *d = (MuxChardev *)s;
+    if (CHARDEV_IS_MUX(s)) {
+        MuxChardev *d = MUX_CHARDEV(s);
         return d->mux_cnt >= 0;
     } else {
         return s->be != NULL;
@@ -954,8 +978,8 @@ void qemu_chr_fe_deinit(CharBackend *b)
         if (b->chr->be == b) {
             b->chr->be = NULL;
         }
-        if (qemu_chr_get_kind(b->chr) == CHARDEV_BACKEND_KIND_MUX) {
-            MuxChardev *d = (MuxChardev *)b->chr;
+        if (CHARDEV_IS_MUX(b->chr)) {
+            MuxChardev *d = MUX_CHARDEV(b->chr);
             d->backends[b->tag] = NULL;
         }
         b->chr = NULL;
@@ -971,6 +995,7 @@ void qemu_chr_fe_set_handlers(CharBackend *b,
                               bool set_open)
 {
     Chardev *s;
+    ChardevClass *cc;
     int fe_open;
 
     s = b->chr;
@@ -978,6 +1003,7 @@ void qemu_chr_fe_set_handlers(CharBackend *b,
         return;
     }
 
+    cc = CHARDEV_GET_CLASS(s);
     if (!opaque && !fd_can_read && !fd_read && !fd_event) {
         fe_open = 0;
         remove_fd_in_watch(s);
@@ -988,8 +1014,8 @@ void qemu_chr_fe_set_handlers(CharBackend *b,
     b->chr_read = fd_read;
     b->chr_event = fd_event;
     b->opaque = opaque;
-    if (s->driver->chr_update_read_handler) {
-        s->driver->chr_update_read_handler(s, context);
+    if (cc->chr_update_read_handler) {
+        cc->chr_update_read_handler(s, context);
     }
 
     if (set_open) {
@@ -1005,7 +1031,7 @@ void qemu_chr_fe_set_handlers(CharBackend *b,
         }
     }
 
-    if (qemu_chr_get_kind(s) == CHARDEV_BACKEND_KIND_MUX) {
+    if (CHARDEV_IS_MUX(s)) {
         mux_chr_set_handlers(s, context);
     }
 }
@@ -1016,7 +1042,7 @@ void qemu_chr_fe_take_focus(CharBackend *b)
         return;
     }
 
-    if (qemu_chr_get_kind(b->chr) == CHARDEV_BACKEND_KIND_MUX) {
+    if (CHARDEV_IS_MUX(b->chr)) {
         mux_set_focus(b->chr, b->tag);
     }
 }
@@ -1193,7 +1219,6 @@ static int io_channel_send(QIOChannel *ioc, const void *buf, size_t len)
     return io_channel_send_full(ioc, buf, len, NULL, 0);
 }
 
-
 typedef struct FDChardev {
     Chardev parent;
     Chardev *chr;
@@ -1201,18 +1226,21 @@ typedef struct FDChardev {
     int max_size;
 } FDChardev;
 
+#define TYPE_CHARDEV_FD "chardev-fd"
+#define FD_CHARDEV(obj) OBJECT_CHECK(FDChardev, (obj), TYPE_CHARDEV_FD)
+
 /* Called with chr_write_lock held.  */
 static int fd_chr_write(Chardev *chr, const uint8_t *buf, int len)
 {
-    FDChardev *s = (FDChardev *)chr;
+    FDChardev *s = FD_CHARDEV(chr);
 
     return io_channel_send(s->ioc_out, buf, len);
 }
 
 static gboolean fd_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
 {
-    Chardev *chr = opaque;
-    FDChardev *s = opaque;
+    Chardev *chr = CHARDEV(opaque);
+    FDChardev *s = FD_CHARDEV(opaque);
     int len;
     uint8_t buf[READ_BUF_LEN];
     ssize_t ret;
@@ -1241,8 +1269,8 @@ static gboolean fd_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
 
 static int fd_chr_read_poll(void *opaque)
 {
-    Chardev *chr = opaque;
-    FDChardev *s = opaque;
+    Chardev *chr = CHARDEV(opaque);
+    FDChardev *s = FD_CHARDEV(opaque);
 
     s->max_size = qemu_chr_be_can_write(chr);
     return s->max_size;
@@ -1250,14 +1278,14 @@ static int fd_chr_read_poll(void *opaque)
 
 static GSource *fd_chr_add_watch(Chardev *chr, GIOCondition cond)
 {
-    FDChardev *s = (FDChardev *)chr;
+    FDChardev *s = FD_CHARDEV(chr);
     return qio_channel_create_watch(s->ioc_out, cond);
 }
 
 static void fd_chr_update_read_handler(Chardev *chr,
                                        GMainContext *context)
 {
-    FDChardev *s = (FDChardev *)chr;
+    FDChardev *s = FD_CHARDEV(chr);
 
     remove_fd_in_watch(chr);
     if (s->ioc_in) {
@@ -1270,7 +1298,7 @@ static void fd_chr_update_read_handler(Chardev *chr,
 
 static void fd_chr_free(struct Chardev *chr)
 {
-    FDChardev *s = (FDChardev *)chr;
+    FDChardev *s = FD_CHARDEV(chr);
 
     remove_fd_in_watch(chr);
     if (s->ioc_in) {
@@ -1284,19 +1312,12 @@ static void fd_chr_free(struct Chardev *chr)
 }
 
 /* open a character device to a unix fd */
-static Chardev *qemu_chr_open_fd(const CharDriver *driver,
-                                 int fd_in, int fd_out,
-                                 ChardevCommon *backend, Error **errp)
+static void qemu_chr_open_fd(Chardev *chr,
+                             int fd_in, int fd_out)
 {
-    Chardev *chr;
-    FDChardev *s;
+    FDChardev *s = FD_CHARDEV(chr);
     char *name;
 
-    chr = qemu_chr_alloc(driver, backend, errp);
-    if (!chr) {
-        return NULL;
-    }
-    s = (FDChardev *)chr;
     s->ioc_in = QIO_CHANNEL(qio_channel_file_new_fd(fd_in));
     name = g_strdup_printf("chardev-file-in-%s", chr->label);
     qio_channel_set_name(QIO_CHANNEL(s->ioc_in), name);
@@ -1307,24 +1328,36 @@ static Chardev *qemu_chr_open_fd(const CharDriver *driver,
     g_free(name);
     qemu_set_nonblock(fd_out);
     s->chr = chr;
+}
 
-    return chr;
+static void char_fd_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->chr_add_watch = fd_chr_add_watch;
+    cc->chr_write = fd_chr_write;
+    cc->chr_update_read_handler = fd_chr_update_read_handler;
+    cc->chr_free = fd_chr_free;
 }
 
-static Chardev *qemu_chr_open_pipe(const CharDriver *driver,
-                                   const char *id,
-                                   ChardevBackend *backend,
-                                   ChardevReturn *ret,
-                                   bool *be_opened,
-                                   Error **errp)
+static const TypeInfo char_fd_type_info = {
+    .name = TYPE_CHARDEV_FD,
+    .parent = TYPE_CHARDEV,
+    .instance_size = sizeof(FDChardev),
+    .class_init = char_fd_class_init,
+    .abstract = true,
+};
+
+static void qemu_chr_open_pipe(Chardev *chr,
+                               ChardevBackend *backend,
+                               bool *be_opened,
+                               Error **errp)
 {
     ChardevHostdev *opts = backend->u.pipe.data;
     int fd_in, fd_out;
     char *filename_in;
     char *filename_out;
     const char *filename = opts->device;
-    ChardevCommon *common = qapi_ChardevHostdev_base(opts);
-
 
     filename_in = g_strdup_printf("%s.in", filename);
     filename_out = g_strdup_printf("%s.out", filename);
@@ -1340,10 +1373,10 @@ static Chardev *qemu_chr_open_pipe(const CharDriver *driver,
         TFR(fd_in = fd_out = qemu_open(filename, O_RDWR | O_BINARY));
         if (fd_in < 0) {
             error_setg_file_open(errp, errno, filename);
-            return NULL;
+            return;
         }
     }
-    return qemu_chr_open_fd(driver, fd_in, fd_out, common, errp);
+    qemu_chr_open_fd(chr, fd_in, fd_out);
 }
 
 /* init terminal so that we can grab keys */
@@ -1395,26 +1428,22 @@ static void qemu_chr_free_stdio(struct Chardev *chr)
     fd_chr_free(chr);
 }
 
-static Chardev *qemu_chr_open_stdio(const CharDriver *driver,
-                                    const char *id,
-                                    ChardevBackend *backend,
-                                    ChardevReturn *ret,
-                                    bool *be_opened,
-                                    Error **errp)
+static void qemu_chr_open_stdio(Chardev *chr,
+                                ChardevBackend *backend,
+                                bool *be_opened,
+                                Error **errp)
 {
     ChardevStdio *opts = backend->u.stdio.data;
-    Chardev *chr;
     struct sigaction act;
-    ChardevCommon *common = qapi_ChardevStdio_base(opts);
 
     if (is_daemonized()) {
         error_setg(errp, "cannot use stdio with -daemonize");
-        return NULL;
+        return;
     }
 
     if (stdio_in_use) {
         error_setg(errp, "cannot use stdio by multiple character devices");
-        return NULL;
+        return;
     }
 
     stdio_in_use = true;
@@ -1427,16 +1456,12 @@ static Chardev *qemu_chr_open_stdio(const CharDriver *driver,
     act.sa_handler = term_stdio_handler;
     sigaction(SIGCONT, &act, NULL);
 
-    chr = qemu_chr_open_fd(driver, 0, 1, common, errp);
-    if (!chr) {
-        return NULL;
-    }
+    qemu_chr_open_fd(chr, 0, 1);
+
     if (opts->has_signal) {
         stdio_allow_signal = opts->signal;
     }
     qemu_chr_set_echo_stdio(chr, false);
-
-    return chr;
 }
 
 #if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \
@@ -1457,13 +1482,15 @@ typedef struct {
     guint open_tag;
 } PtyChardev;
 
+#define PTY_CHARDEV(obj) OBJECT_CHECK(PtyChardev, (obj), TYPE_CHARDEV_PTY)
+
 static void pty_chr_update_read_handler_locked(Chardev *chr);
 static void pty_chr_state(Chardev *chr, int connected);
 
 static gboolean pty_chr_timer(gpointer opaque)
 {
-    struct Chardev *chr = opaque;
-    PtyChardev *s = opaque;
+    struct Chardev *chr = CHARDEV(opaque);
+    PtyChardev *s = PTY_CHARDEV(opaque);
 
     qemu_mutex_lock(&chr->chr_write_lock);
     s->timer_tag = 0;
@@ -1479,7 +1506,7 @@ static gboolean pty_chr_timer(gpointer opaque)
 /* Called with chr_write_lock held.  */
 static void pty_chr_rearm_timer(Chardev *chr, int ms)
 {
-    PtyChardev *s = (PtyChardev *)chr;
+    PtyChardev *s = PTY_CHARDEV(chr);
     char *name;
 
     if (s->timer_tag) {
@@ -1501,7 +1528,7 @@ static void pty_chr_rearm_timer(Chardev *chr, int ms)
 /* Called with chr_write_lock held.  */
 static void pty_chr_update_read_handler_locked(Chardev *chr)
 {
-    PtyChardev *s = (PtyChardev *)chr;
+    PtyChardev *s = PTY_CHARDEV(chr);
     GPollFD pfd;
     int rc;
     QIOChannelFile *fioc = QIO_CHANNEL_FILE(s->ioc);
@@ -1530,9 +1557,9 @@ static void pty_chr_update_read_handler(Chardev *chr,
 }
 
 /* Called with chr_write_lock held.  */
-static int pty_chr_write(Chardev *chr, const uint8_t *buf, int len)
+static int char_pty_chr_write(Chardev *chr, const uint8_t *buf, int len)
 {
-    PtyChardev *s = (PtyChardev *)chr;
+    PtyChardev *s = PTY_CHARDEV(chr);
 
     if (!s->connected) {
         /* guest sends data, check for (re-)connect */
@@ -1546,7 +1573,7 @@ static int pty_chr_write(Chardev *chr, const uint8_t *buf, int len)
 
 static GSource *pty_chr_add_watch(Chardev *chr, GIOCondition cond)
 {
-    PtyChardev *s = (PtyChardev *)chr;
+    PtyChardev *s = PTY_CHARDEV(chr);
     if (!s->connected) {
         return NULL;
     }
@@ -1555,8 +1582,8 @@ static GSource *pty_chr_add_watch(Chardev *chr, GIOCondition cond)
 
 static int pty_chr_read_poll(void *opaque)
 {
-    Chardev *chr = opaque;
-    PtyChardev *s = opaque;
+    Chardev *chr = CHARDEV(opaque);
+    PtyChardev *s = PTY_CHARDEV(opaque);
 
     s->read_bytes = qemu_chr_be_can_write(chr);
     return s->read_bytes;
@@ -1564,8 +1591,8 @@ static int pty_chr_read_poll(void *opaque)
 
 static gboolean pty_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
 {
-    Chardev *chr = opaque;
-    PtyChardev *s = opaque;
+    Chardev *chr = CHARDEV(opaque);
+    PtyChardev *s = PTY_CHARDEV(opaque);
     gsize len;
     uint8_t buf[READ_BUF_LEN];
     ssize_t ret;
@@ -1589,8 +1616,8 @@ static gboolean pty_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
 
 static gboolean qemu_chr_be_generic_open_func(gpointer opaque)
 {
-    Chardev *chr = opaque;
-    PtyChardev *s = opaque;
+    Chardev *chr = CHARDEV(opaque);
+    PtyChardev *s = PTY_CHARDEV(opaque);
 
     s->open_tag = 0;
     qemu_chr_be_generic_open(chr);
@@ -1600,7 +1627,7 @@ static gboolean qemu_chr_be_generic_open_func(gpointer opaque)
 /* Called with chr_write_lock held.  */
 static void pty_chr_state(Chardev *chr, int connected)
 {
-    PtyChardev *s = (PtyChardev *)chr;
+    PtyChardev *s = PTY_CHARDEV(chr);
 
     if (!connected) {
         if (s->open_tag) {
@@ -1634,7 +1661,7 @@ static void pty_chr_state(Chardev *chr, int connected)
 
 static void pty_chr_free(struct Chardev *chr)
 {
-    PtyChardev *s = (PtyChardev *)chr;
+    PtyChardev *s = PTY_CHARDEV(chr);
 
     qemu_mutex_lock(&chr->chr_write_lock);
     pty_chr_state(chr, 0);
@@ -1647,61 +1674,58 @@ static void pty_chr_free(struct Chardev *chr)
     qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
 }
 
-static Chardev *qemu_chr_open_pty(const CharDriver *driver,
-                                  const char *id,
-                                  ChardevBackend *backend,
-                                  ChardevReturn *ret,
-                                  bool *be_opened,
-                                  Error **errp)
+static void char_pty_open(Chardev *chr,
+                          ChardevBackend *backend,
+                          bool *be_opened,
+                          Error **errp)
 {
-    Chardev *chr;
     PtyChardev *s;
     int master_fd, slave_fd;
     char pty_name[PATH_MAX];
-    ChardevCommon *common = backend->u.pty.data;
     char *name;
 
     master_fd = qemu_openpty_raw(&slave_fd, pty_name);
     if (master_fd < 0) {
         error_setg_errno(errp, errno, "Failed to create PTY");
-        return NULL;
+        return;
     }
 
     close(slave_fd);
     qemu_set_nonblock(master_fd);
 
-    chr = qemu_chr_alloc(driver, common, errp);
-    if (!chr) {
-        close(master_fd);
-        return NULL;
-    }
-
     chr->filename = g_strdup_printf("pty:%s", pty_name);
-    ret->pty = g_strdup(pty_name);
-    ret->has_pty = true;
-
     error_report("char device redirected to %s (label %s)",
-                 pty_name, id);
+                 pty_name, chr->label);
 
-    s = (PtyChardev *)chr;
+    s = PTY_CHARDEV(chr);
     s->ioc = QIO_CHANNEL(qio_channel_file_new_fd(master_fd));
     name = g_strdup_printf("chardev-pty-%s", chr->label);
     qio_channel_set_name(QIO_CHANNEL(s->ioc), name);
     g_free(name);
     s->timer_tag = 0;
     *be_opened = false;
-
-    return chr;
 }
 
 static const CharDriver pty_driver = {
-    .instance_size = sizeof(PtyChardev),
     .kind = CHARDEV_BACKEND_KIND_PTY,
-    .create = qemu_chr_open_pty,
-    .chr_write = pty_chr_write,
-    .chr_update_read_handler = pty_chr_update_read_handler,
-    .chr_add_watch = pty_chr_add_watch,
-    .chr_free = pty_chr_free,
+};
+
+static void char_pty_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->open = char_pty_open;
+    cc->chr_write = char_pty_chr_write;
+    cc->chr_update_read_handler = pty_chr_update_read_handler;
+    cc->chr_add_watch = pty_chr_add_watch;
+    cc->chr_free = pty_chr_free;
+}
+
+static const TypeInfo char_pty_type_info = {
+    .name = TYPE_CHARDEV_PTY,
+    .parent = TYPE_CHARDEV,
+    .instance_size = sizeof(PtyChardev),
+    .class_init = char_pty_class_init,
 };
 
 static void tty_serial_init(int fd, int speed,
@@ -1821,7 +1845,7 @@ static void tty_serial_init(int fd, int speed,
 
 static int tty_serial_ioctl(Chardev *chr, int cmd, void *arg)
 {
-    FDChardev *s = (FDChardev *)chr;
+    FDChardev *s = FD_CHARDEV(chr);
     QIOChannelFile *fioc = QIO_CHANNEL_FILE(s->ioc_in);
 
     switch(cmd) {
@@ -1905,6 +1929,9 @@ typedef struct {
     int mode;
 } ParallelChardev;
 
+#define PARALLEL_CHARDEV(obj) \
+    OBJECT_CHECK(ParallelChardev, (obj), TYPE_CHARDEV_PARALLEL)
+
 static int pp_hw_mode(ParallelChardev *s, uint16_t mode)
 {
     if (s->mode != mode) {
@@ -1918,7 +1945,7 @@ static int pp_hw_mode(ParallelChardev *s, uint16_t mode)
 
 static int pp_ioctl(Chardev *chr, int cmd, void *arg)
 {
-    ParallelChardev *drv = (ParallelChardev *)chr;
+    ParallelChardev *drv = PARALLEL_CHARDEV(chr);
     int fd = drv->fd;
     uint8_t b;
 
@@ -1999,7 +2026,7 @@ static int pp_ioctl(Chardev *chr, int cmd, void *arg)
 
 static void pp_free(Chardev *chr)
 {
-    ParallelChardev *drv = (ParallelChardev *)chr;
+    ParallelChardev *drv = PARALLEL_CHARDEV(chr);
     int fd = drv->fd;
 
     pp_hw_mode(drv, IEEE1284_MODE_COMPAT);
@@ -2008,31 +2035,21 @@ static void pp_free(Chardev *chr)
     qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
 }
 
-static Chardev *qemu_chr_open_pp_fd(const CharDriver *driver,
-                                    int fd,
-                                    ChardevCommon *backend,
-                                    bool *be_opened,
-                                    Error **errp)
+static void qemu_chr_open_pp_fd(Chardev *chr,
+                                int fd,
+                                bool *be_opened,
+                                Error **errp)
 {
-    Chardev *chr;
-    ParallelChardev *drv;
+    ParallelChardev *drv = PARALLEL_CHARDEV(chr);
 
     if (ioctl(fd, PPCLAIM) < 0) {
         error_setg_errno(errp, errno, "not a parallel port");
         close(fd);
-        return NULL;
-    }
-
-    chr = qemu_chr_alloc(driver, backend, errp);
-    if (!chr) {
-        return NULL;
+        return;
     }
 
-    drv = (ParallelChardev *)chr;
     drv->fd = fd;
     drv->mode = IEEE1284_MODE_COMPAT;
-
-    return chr;
 }
 #endif /* __linux__ */
 
@@ -2045,9 +2062,12 @@ typedef struct {
     int fd;
 } ParallelChardev;
 
+#define PARALLEL_CHARDEV(obj)                                   \
+    OBJECT_CHECK(ParallelChardev, (obj), TYPE_CHARDEV_PARALLEL)
+
 static int pp_ioctl(Chardev *chr, int cmd, void *arg)
 {
-    ParallelChardev *drv = (ParallelChardev *)chr;
+    ParallelChardev *drv = PARALLEL_CHARDEV(chr);
     uint8_t b;
 
     switch (cmd) {
@@ -2087,23 +2107,14 @@ static int pp_ioctl(Chardev *chr, int cmd, void *arg)
     return 0;
 }
 
-static Chardev *qemu_chr_open_pp_fd(const CharDriver *driver,
-                                    int fd,
-                                    ChardevCommon *backend,
-                                    bool *be_opened,
-                                    Error **errp)
+static void qemu_chr_open_pp_fd(Chardev *chr,
+                                int fd,
+                                bool *be_opened,
+                                Error **errp)
 {
-    Chardev *chr;
-    ParallelChardev *drv;
-
-    chr = qemu_chr_alloc(driver, backend, errp);
-    if (!chr) {
-        return NULL;
-    }
-    drv = (ParallelChardev *)chr;
+    ParallelChardev *drv = PARALLEL_CHARDEV(chr);
     drv->fd = fd;
     *be_opened = false;
-    return chr;
 }
 #endif
 
@@ -2123,6 +2134,9 @@ typedef struct {
     OVERLAPPED osend;
 } WinChardev;
 
+#define TYPE_CHARDEV_WIN "chardev-win"
+#define WIN_CHARDEV(obj) OBJECT_CHECK(WinChardev, (obj), TYPE_CHARDEV_WIN)
+
 typedef struct {
     Chardev parent;
     HANDLE  hStdIn;
@@ -2132,6 +2146,10 @@ typedef struct {
     uint8_t win_stdio_buf;
 } WinStdioChardev;
 
+#define TYPE_CHARDEV_WIN_STDIO "chardev-win-stdio"
+#define WIN_STDIO_CHARDEV(obj)                                  \
+    OBJECT_CHECK(WinStdioChardev, (obj), TYPE_CHARDEV_WIN_STDIO)
+
 #define NSENDBUF 2048
 #define NRECVBUF 2048
 #define MAXCONNECT 1
@@ -2142,7 +2160,7 @@ static int win_chr_pipe_poll(void *opaque);
 
 static void win_chr_free(Chardev *chr)
 {
-    WinChardev *s = (WinChardev *)chr;
+    WinChardev *s = WIN_CHARDEV(chr);
 
     if (s->hsend) {
         CloseHandle(s->hsend);
@@ -2166,7 +2184,7 @@ static void win_chr_free(Chardev *chr)
 
 static int win_chr_init(Chardev *chr, const char *filename, Error **errp)
 {
-    WinChardev *s = (WinChardev *)chr;
+    WinChardev *s = WIN_CHARDEV(chr);
     COMMCONFIG comcfg;
     COMMTIMEOUTS cto = { 0, 0, 0, 0, 0};
     COMSTAT comstat;
@@ -2234,7 +2252,7 @@ static int win_chr_init(Chardev *chr, const char *filename, Error **errp)
 /* Called with chr_write_lock held.  */
 static int win_chr_write(Chardev *chr, const uint8_t *buf, int len1)
 {
-    WinChardev *s = (WinChardev *)chr;
+    WinChardev *s = WIN_CHARDEV(chr);
     DWORD len, ret, size, err;
 
     len = len1;
@@ -2268,7 +2286,7 @@ static int win_chr_write(Chardev *chr, const uint8_t *buf, int len1)
 
 static int win_chr_read_poll(Chardev *chr)
 {
-    WinChardev *s = (WinChardev *)chr;
+    WinChardev *s = WIN_CHARDEV(chr);
 
     s->max_size = qemu_chr_be_can_write(chr);
     return s->max_size;
@@ -2276,7 +2294,8 @@ static int win_chr_read_poll(Chardev *chr)
 
 static void win_chr_readfile(Chardev *chr)
 {
-    WinChardev *s = (WinChardev *)chr;
+    WinChardev *s = WIN_CHARDEV(chr);
+
     int ret, err;
     uint8_t buf[READ_BUF_LEN];
     DWORD size;
@@ -2298,7 +2317,7 @@ static void win_chr_readfile(Chardev *chr)
 
 static void win_chr_read(Chardev *chr)
 {
-    WinChardev *s = (WinChardev *)chr;
+    WinChardev *s = WIN_CHARDEV(chr);
 
     if (s->len > s->max_size)
         s->len = s->max_size;
@@ -2310,8 +2329,8 @@ static void win_chr_read(Chardev *chr)
 
 static int win_chr_poll(void *opaque)
 {
-    Chardev *chr = opaque;
-    WinChardev *s = opaque;
+    Chardev *chr = CHARDEV(opaque);
+    WinChardev *s = WIN_CHARDEV(opaque);
     COMSTAT status;
     DWORD comerr;
 
@@ -2327,8 +2346,8 @@ static int win_chr_poll(void *opaque)
 
 static int win_chr_pipe_poll(void *opaque)
 {
-    Chardev *chr = opaque;
-    WinChardev *s = opaque;
+    Chardev *chr = CHARDEV(opaque);
+    WinChardev *s = WIN_CHARDEV(opaque);
     DWORD size;
 
     PeekNamedPipe(s->hcom, NULL, 0, NULL, &size, NULL);
@@ -2344,7 +2363,7 @@ static int win_chr_pipe_poll(void *opaque)
 static int win_chr_pipe_init(Chardev *chr, const char *filename,
                              Error **errp)
 {
-    WinChardev *s = (WinChardev *)chr;
+    WinChardev *s = WIN_CHARDEV(chr);
     OVERLAPPED ov;
     int ret;
     DWORD size;
@@ -2406,65 +2425,66 @@ static int win_chr_pipe_init(Chardev *chr, const char *filename,
 }
 
 
-static Chardev *qemu_chr_open_pipe(const CharDriver *driver,
-                                   const char *id,
-                                   ChardevBackend *backend,
-                                   ChardevReturn *ret,
-                                   bool *be_opened,
-                                   Error **errp)
+static void qemu_chr_open_pipe(Chardev *chr,
+                               ChardevBackend *backend,
+                               bool *be_opened,
+                               Error **errp)
 {
     ChardevHostdev *opts = backend->u.pipe.data;
     const char *filename = opts->device;
-    Chardev *chr;
-    ChardevCommon *common = qapi_ChardevHostdev_base(opts);
-
-    chr = qemu_chr_alloc(driver, common, errp);
-    if (!chr) {
-        return NULL;
-    }
 
     if (win_chr_pipe_init(chr, filename, errp) < 0) {
-        qemu_chr_free_common(chr);
-        return NULL;
+        return;
     }
-    return chr;
 }
 
-static Chardev *qemu_chr_open_win_file(const CharDriver *driver,
-                                       HANDLE fd_out,
-                                       ChardevCommon *backend,
-                                       Error **errp)
+static void qemu_chr_open_win_file(Chardev *chr, HANDLE fd_out)
 {
-    Chardev *chr;
-    WinChardev *s;
+    WinChardev *s = WIN_CHARDEV(chr);
 
-    chr = qemu_chr_alloc(driver, backend, errp);
-    if (!chr) {
-        return NULL;
-    }
-    s = (WinChardev *)chr;
     s->hcom = fd_out;
-    return chr;
 }
 
-static Chardev *qemu_chr_open_win_con(const CharDriver *driver,
-                                      const char *id,
-                                      ChardevBackend *backend,
-                                      ChardevReturn *ret,
-                                      bool *be_opened,
-                                      Error **errp)
+static void char_win_class_init(ObjectClass *oc, void *data)
 {
-    ChardevCommon *common = backend->u.console.data;
-    return qemu_chr_open_win_file(driver,
-                                  GetStdHandle(STD_OUTPUT_HANDLE),
-                                  common, errp);
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->chr_write = win_chr_write;
+    cc->chr_free = win_chr_free;
 }
 
-static const CharDriver console_driver = {
+static const TypeInfo char_win_type_info = {
+    .name = TYPE_CHARDEV_WIN,
+    .parent = TYPE_CHARDEV,
     .instance_size = sizeof(WinChardev),
+    .class_init = char_win_class_init,
+    .abstract = true,
+};
+
+static void qemu_chr_open_win_con(Chardev *chr,
+                                  ChardevBackend *backend,
+                                  bool *be_opened,
+                                  Error **errp)
+{
+    qemu_chr_open_win_file(chr, GetStdHandle(STD_OUTPUT_HANDLE));
+}
+
+static const CharDriver console_driver = {
     .kind = CHARDEV_BACKEND_KIND_CONSOLE,
-    .create = qemu_chr_open_win_con,
-    .chr_write = win_chr_write,
+};
+
+static void char_console_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->open = qemu_chr_open_win_con;
+    cc->chr_free = NULL;
+}
+
+static const TypeInfo char_console_type_info = {
+    .name = TYPE_CHARDEV_CONSOLE,
+    .parent = TYPE_CHARDEV_WIN,
+    .class_init = char_console_class_init,
 };
 
 static int win_stdio_write(Chardev *chr, const uint8_t *buf, int len)
@@ -2488,8 +2508,8 @@ static int win_stdio_write(Chardev *chr, const uint8_t *buf, int len)
 
 static void win_stdio_wait_func(void *opaque)
 {
-    Chardev   *chr   = opaque;
-    WinStdioChardev *stdio = opaque;
+    Chardev *chr = CHARDEV(opaque);
+    WinStdioChardev *stdio = WIN_STDIO_CHARDEV(opaque);
     INPUT_RECORD       buf[4];
     int                ret;
     DWORD              dwSize;
@@ -2522,7 +2542,7 @@ static void win_stdio_wait_func(void *opaque)
 
 static DWORD WINAPI win_stdio_thread(LPVOID param)
 {
-    WinStdioChardev *stdio = param;
+    WinStdioChardev *stdio = WIN_STDIO_CHARDEV(param);
     int                ret;
     DWORD              dwSize;
 
@@ -2560,8 +2580,8 @@ static DWORD WINAPI win_stdio_thread(LPVOID param)
 
 static void win_stdio_thread_wait_func(void *opaque)
 {
-    Chardev   *chr   = opaque;
-    WinStdioChardev *stdio = opaque;
+    Chardev *chr = CHARDEV(opaque);
+    WinStdioChardev *stdio = WIN_STDIO_CHARDEV(opaque);
 
     if (qemu_chr_be_can_write(chr)) {
         qemu_chr_be_write(chr, &stdio->win_stdio_buf, 1);
@@ -2572,7 +2592,7 @@ static void win_stdio_thread_wait_func(void *opaque)
 
 static void qemu_chr_set_echo_win_stdio(Chardev *chr, bool echo)
 {
-    WinStdioChardev *stdio  = (WinStdioChardev *)chr;
+    WinStdioChardev *stdio = WIN_STDIO_CHARDEV(chr);
     DWORD              dwMode = 0;
 
     GetConsoleMode(stdio->hStdIn, &dwMode);
@@ -2586,7 +2606,7 @@ static void qemu_chr_set_echo_win_stdio(Chardev *chr, bool echo)
 
 static void win_stdio_free(Chardev *chr)
 {
-    WinStdioChardev *stdio = (WinStdioChardev *)chr;
+    WinStdioChardev *stdio = WIN_STDIO_CHARDEV(chr);
 
     if (stdio->hInputReadyEvent != INVALID_HANDLE_VALUE) {
         CloseHandle(stdio->hInputReadyEvent);
@@ -2599,29 +2619,26 @@ static void win_stdio_free(Chardev *chr)
     }
 }
 
-static Chardev *qemu_chr_open_stdio(const CharDriver *driver,
-                                    const char *id,
-                                    ChardevBackend *backend,
-                                    ChardevReturn *ret,
-                                    bool *be_opened,
-                                    Error **errp)
+static const TypeInfo char_win_stdio_type_info = {
+    .name = TYPE_CHARDEV_WIN_STDIO,
+    .parent = TYPE_CHARDEV,
+    .instance_size = sizeof(WinStdioChardev),
+    .abstract = true,
+};
+
+static void qemu_chr_open_stdio(Chardev *chr,
+                                ChardevBackend *backend,
+                                bool *be_opened,
+                                Error **errp)
 {
-    Chardev   *chr;
-    WinStdioChardev *stdio;
+    WinStdioChardev *stdio = WIN_STDIO_CHARDEV(chr);
     DWORD              dwMode;
     int                is_console = 0;
-    ChardevCommon *common = qapi_ChardevStdio_base(backend->u.stdio.data);
-
-    chr = qemu_chr_alloc(driver, common, errp);
-    if (!chr) {
-        return NULL;
-    }
-    stdio = (WinStdioChardev *)chr;
 
     stdio->hStdIn = GetStdHandle(STD_INPUT_HANDLE);
     if (stdio->hStdIn == INVALID_HANDLE_VALUE) {
         error_setg(errp, "cannot open stdio: invalid handle");
-        return NULL;
+        return;
     }
 
     is_console = GetConsoleMode(stdio->hStdIn, &dwMode) != 0;
@@ -2668,7 +2685,7 @@ static Chardev *qemu_chr_open_stdio(const CharDriver *driver,
 
     qemu_chr_set_echo_win_stdio(chr, false);
 
-    return chr;
+    return;
 
 err3:
     qemu_del_wait_object(stdio->hInputReadyEvent, NULL, NULL);
@@ -2677,7 +2694,6 @@ err2:
     CloseHandle(stdio->hInputDoneEvent);
 err1:
     qemu_del_wait_object(stdio->hStdIn, NULL, NULL);
-    return NULL;
 }
 #endif /* !_WIN32 */
 
@@ -2693,10 +2709,12 @@ typedef struct {
     int max_size;
 } UdpChardev;
 
+#define UDP_CHARDEV(obj) OBJECT_CHECK(UdpChardev, (obj), TYPE_CHARDEV_UDP)
+
 /* Called with chr_write_lock held.  */
 static int udp_chr_write(Chardev *chr, const uint8_t *buf, int len)
 {
-    UdpChardev *s = (UdpChardev *)chr;
+    UdpChardev *s = UDP_CHARDEV(chr);
 
     return qio_channel_write(
         s->ioc, (const char *)buf, len, NULL);
@@ -2704,8 +2722,8 @@ static int udp_chr_write(Chardev *chr, const uint8_t *buf, int len)
 
 static int udp_chr_read_poll(void *opaque)
 {
-    Chardev *chr = opaque;
-    UdpChardev *s = opaque;
+    Chardev *chr = CHARDEV(opaque);
+    UdpChardev *s = UDP_CHARDEV(opaque);
 
     s->max_size = qemu_chr_be_can_write(chr);
 
@@ -2722,8 +2740,8 @@ static int udp_chr_read_poll(void *opaque)
 
 static gboolean udp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
 {
-    Chardev *chr = opaque;
-    UdpChardev *s = opaque;
+    Chardev *chr = CHARDEV(opaque);
+    UdpChardev *s = UDP_CHARDEV(opaque);
     ssize_t ret;
 
     if (s->max_size == 0) {
@@ -2750,7 +2768,7 @@ static gboolean udp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
 static void udp_chr_update_read_handler(Chardev *chr,
                                         GMainContext *context)
 {
-    UdpChardev *s = (UdpChardev *)chr;
+    UdpChardev *s = UDP_CHARDEV(chr);
 
     remove_fd_in_watch(chr);
     if (s->ioc) {
@@ -2763,7 +2781,7 @@ static void udp_chr_update_read_handler(Chardev *chr,
 
 static void udp_chr_free(Chardev *chr)
 {
-    UdpChardev *s = (UdpChardev *)chr;
+    UdpChardev *s = UDP_CHARDEV(chr);
 
     remove_fd_in_watch(chr);
     if (s->ioc) {
@@ -2801,11 +2819,14 @@ typedef struct {
     bool connect_err_reported;
 } SocketChardev;
 
+#define SOCKET_CHARDEV(obj)                                     \
+    OBJECT_CHECK(SocketChardev, (obj), TYPE_CHARDEV_SOCKET)
+
 static gboolean socket_reconnect_timeout(gpointer opaque);
 
 static void qemu_chr_socket_restart_timer(Chardev *chr)
 {
-    SocketChardev *s = (SocketChardev *)chr;
+    SocketChardev *s = SOCKET_CHARDEV(chr);
     char *name;
 
     assert(s->connected == 0);
@@ -2819,7 +2840,7 @@ static void qemu_chr_socket_restart_timer(Chardev *chr)
 static void check_report_connect_error(Chardev *chr,
                                        Error *err)
 {
-    SocketChardev *s = (SocketChardev *)chr;
+    SocketChardev *s = SOCKET_CHARDEV(chr);
 
     if (!s->connect_err_reported) {
         error_report("Unable to connect character device %s: %s",
@@ -2836,7 +2857,7 @@ static gboolean tcp_chr_accept(QIOChannel *chan,
 /* Called with chr_write_lock held.  */
 static int tcp_chr_write(Chardev *chr, const uint8_t *buf, int len)
 {
-    SocketChardev *s = (SocketChardev *)chr;
+    SocketChardev *s = SOCKET_CHARDEV(chr);
 
     if (s->connected) {
         int ret =  io_channel_send_full(s->ioc, buf, len,
@@ -2859,8 +2880,8 @@ static int tcp_chr_write(Chardev *chr, const uint8_t *buf, int len)
 
 static int tcp_chr_read_poll(void *opaque)
 {
-    Chardev *chr = opaque;
-    SocketChardev *s = opaque;
+    Chardev *chr = CHARDEV(opaque);
+    SocketChardev *s = SOCKET_CHARDEV(opaque);
     if (!s->connected)
         return 0;
     s->max_size = qemu_chr_be_can_write(chr);
@@ -2919,7 +2940,7 @@ static void tcp_chr_process_IAC_bytes(Chardev *chr,
 
 static int tcp_get_msgfds(Chardev *chr, int *fds, int num)
 {
-    SocketChardev *s = (SocketChardev *)chr;
+    SocketChardev *s = SOCKET_CHARDEV(chr);
 
     int to_copy = (s->read_msgfds_num < num) ? s->read_msgfds_num : num;
 
@@ -2945,7 +2966,7 @@ static int tcp_get_msgfds(Chardev *chr, int *fds, int num)
 
 static int tcp_set_msgfds(Chardev *chr, int *fds, int num)
 {
-    SocketChardev *s = (SocketChardev *)chr;
+    SocketChardev *s = SOCKET_CHARDEV(chr);
 
     /* clear old pending fd array */
     g_free(s->write_msgfds);
@@ -2970,7 +2991,7 @@ static int tcp_set_msgfds(Chardev *chr, int *fds, int num)
 
 static ssize_t tcp_chr_recv(Chardev *chr, char *buf, size_t len)
 {
-    SocketChardev *s = (SocketChardev *)chr;
+    SocketChardev *s = SOCKET_CHARDEV(chr);
     struct iovec iov = { .iov_base = buf, .iov_len = len };
     int ret;
     size_t i;
@@ -3027,13 +3048,13 @@ static ssize_t tcp_chr_recv(Chardev *chr, char *buf, size_t len)
 
 static GSource *tcp_chr_add_watch(Chardev *chr, GIOCondition cond)
 {
-    SocketChardev *s = (SocketChardev *)chr;
+    SocketChardev *s = SOCKET_CHARDEV(chr);
     return qio_channel_create_watch(s->ioc, cond);
 }
 
 static void tcp_chr_free_connection(Chardev *chr)
 {
-    SocketChardev *s = (SocketChardev *)chr;
+    SocketChardev *s = SOCKET_CHARDEV(chr);
     int i;
 
     if (!s->connected) {
@@ -3062,7 +3083,7 @@ static void tcp_chr_free_connection(Chardev *chr)
 
 static void tcp_chr_disconnect(Chardev *chr)
 {
-    SocketChardev *s = (SocketChardev *)chr;
+    SocketChardev *s = SOCKET_CHARDEV(chr);
 
     if (!s->connected) {
         return;
@@ -3084,8 +3105,8 @@ static void tcp_chr_disconnect(Chardev *chr)
 
 static gboolean tcp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
 {
-    Chardev *chr = opaque;
-    SocketChardev *s = opaque;
+    Chardev *chr = CHARDEV(opaque);
+    SocketChardev *s = SOCKET_CHARDEV(opaque);
     uint8_t buf[READ_BUF_LEN];
     int len, size;
 
@@ -3111,7 +3132,7 @@ static gboolean tcp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
 
 static int tcp_chr_sync_read(Chardev *chr, const uint8_t *buf, int len)
 {
-    SocketChardev *s = (SocketChardev *)chr;
+    SocketChardev *s = SOCKET_CHARDEV(chr);
     int size;
 
     if (!s->connected) {
@@ -3129,8 +3150,8 @@ static int tcp_chr_sync_read(Chardev *chr, const uint8_t *buf, int len)
 
 static void tcp_chr_connect(void *opaque)
 {
-    Chardev *chr = opaque;
-    SocketChardev *s = opaque;
+    Chardev *chr = CHARDEV(opaque);
+    SocketChardev *s = SOCKET_CHARDEV(opaque);
 
     g_free(chr->filename);
     chr->filename = sockaddr_to_str(
@@ -3151,7 +3172,7 @@ static void tcp_chr_connect(void *opaque)
 static void tcp_chr_update_read_handler(Chardev *chr,
                                         GMainContext *context)
 {
-    SocketChardev *s = (SocketChardev *)chr;
+    SocketChardev *s = SOCKET_CHARDEV(chr);
 
     if (!s->connected) {
         return;
@@ -3202,7 +3223,7 @@ static gboolean tcp_chr_telnet_init_io(QIOChannel *ioc,
 
 static void tcp_chr_telnet_init(Chardev *chr)
 {
-    SocketChardev *s = (SocketChardev *)chr;
+    SocketChardev *s = SOCKET_CHARDEV(chr);
     TCPCharDriverTelnetInit *init =
         g_new0(TCPCharDriverTelnetInit, 1);
     size_t n = 0;
@@ -3253,7 +3274,7 @@ static void tcp_chr_tls_handshake(QIOTask *task,
 
 static void tcp_chr_tls_init(Chardev *chr)
 {
-    SocketChardev *s = (SocketChardev *)chr;
+    SocketChardev *s = SOCKET_CHARDEV(chr);
     QIOChannelTLS *tioc;
     Error *err = NULL;
     gchar *name;
@@ -3292,7 +3313,7 @@ static void tcp_chr_tls_init(Chardev *chr)
 static void tcp_chr_set_client_ioc_name(Chardev *chr,
                                         QIOChannelSocket *sioc)
 {
-    SocketChardev *s = (SocketChardev *)chr;
+    SocketChardev *s = SOCKET_CHARDEV(chr);
     char *name;
     name = g_strdup_printf("chardev-tcp-%s-%s",
                            s->is_listen ? "server" : "client",
@@ -3304,7 +3325,7 @@ static void tcp_chr_set_client_ioc_name(Chardev *chr,
 
 static int tcp_chr_new_client(Chardev *chr, QIOChannelSocket *sioc)
 {
-    SocketChardev *s = (SocketChardev *)chr;
+    SocketChardev *s = SOCKET_CHARDEV(chr);
 
     if (s->ioc != NULL) {
 	return -1;
@@ -3358,7 +3379,7 @@ static gboolean tcp_chr_accept(QIOChannel *channel,
                                GIOCondition cond,
                                void *opaque)
 {
-    Chardev *chr = opaque;
+    Chardev *chr = CHARDEV(opaque);
     QIOChannelSocket *sioc;
 
     sioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(channel),
@@ -3376,7 +3397,7 @@ static gboolean tcp_chr_accept(QIOChannel *channel,
 
 static int tcp_chr_wait_connected(Chardev *chr, Error **errp)
 {
-    SocketChardev *s = (SocketChardev *)chr;
+    SocketChardev *s = SOCKET_CHARDEV(chr);
     QIOChannelSocket *sioc;
 
     /* It can't wait on s->connected, since it is set asynchronously
@@ -3405,8 +3426,10 @@ static int tcp_chr_wait_connected(Chardev *chr, Error **errp)
 
 static int qemu_chr_wait_connected(Chardev *chr, Error **errp)
 {
-    if (chr->driver->chr_wait_connected) {
-        return chr->driver->chr_wait_connected(chr, errp);
+    ChardevClass *cc = CHARDEV_GET_CLASS(chr);
+
+    if (cc->chr_wait_connected) {
+        return cc->chr_wait_connected(chr, errp);
     }
 
     return 0;
@@ -3424,7 +3447,7 @@ int qemu_chr_fe_wait_connected(CharBackend *be, Error **errp)
 
 static void tcp_chr_free(Chardev *chr)
 {
-    SocketChardev *s = (SocketChardev *)chr;
+    SocketChardev *s = SOCKET_CHARDEV(chr);
 
     tcp_chr_free_connection(chr);
 
@@ -3451,8 +3474,8 @@ static void tcp_chr_free(Chardev *chr)
 static void qemu_chr_socket_connected(QIOTask *task, void *opaque)
 {
     QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(qio_task_get_source(task));
-    Chardev *chr = opaque;
-    SocketChardev *s = (SocketChardev *)chr;
+    Chardev *chr = CHARDEV(opaque);
+    SocketChardev *s = SOCKET_CHARDEV(chr);
     Error *err = NULL;
 
     if (qio_task_propagate_error(task, &err)) {
@@ -3480,9 +3503,12 @@ typedef struct {
     uint8_t *cbuf;
 } RingBufChardev;
 
+#define RINGBUF_CHARDEV(obj)                                    \
+    OBJECT_CHECK(RingBufChardev, (obj), TYPE_CHARDEV_RINGBUF)
+
 static size_t ringbuf_count(const Chardev *chr)
 {
-    const RingBufChardev *d = (RingBufChardev *)chr;
+    const RingBufChardev *d = RINGBUF_CHARDEV(chr);
 
     return d->prod - d->cons;
 }
@@ -3490,7 +3516,7 @@ static size_t ringbuf_count(const Chardev *chr)
 /* Called with chr_write_lock held.  */
 static int ringbuf_chr_write(Chardev *chr, const uint8_t *buf, int len)
 {
-    RingBufChardev *d = (RingBufChardev *)chr;
+    RingBufChardev *d = RINGBUF_CHARDEV(chr);
     int i;
 
     if (!buf || (len < 0)) {
@@ -3509,7 +3535,7 @@ static int ringbuf_chr_write(Chardev *chr, const uint8_t *buf, int len)
 
 static int ringbuf_chr_read(Chardev *chr, uint8_t *buf, int len)
 {
-    RingBufChardev *d = (RingBufChardev *)chr;
+    RingBufChardev *d = RINGBUF_CHARDEV(chr);
     int i;
 
     qemu_mutex_lock(&chr->chr_write_lock);
@@ -3523,51 +3549,30 @@ static int ringbuf_chr_read(Chardev *chr, uint8_t *buf, int len)
 
 static void ringbuf_chr_free(struct Chardev *chr)
 {
-    RingBufChardev *d = (RingBufChardev *)chr;
+    RingBufChardev *d = RINGBUF_CHARDEV(chr);
 
     g_free(d->cbuf);
 }
 
-static Chardev *qemu_chr_open_ringbuf(const CharDriver *driver,
-                                      const char *id,
-                                      ChardevBackend *backend,
-                                      ChardevReturn *ret,
-                                      bool *be_opened,
-                                      Error **errp)
+static void qemu_chr_open_ringbuf(Chardev *chr,
+                                  ChardevBackend *backend,
+                                  bool *be_opened,
+                                  Error **errp)
 {
     ChardevRingbuf *opts = backend->u.ringbuf.data;
-    ChardevCommon *common = qapi_ChardevRingbuf_base(opts);
-    Chardev *chr;
-    RingBufChardev *d;
-
-    chr = qemu_chr_alloc(driver, common, errp);
-    if (!chr) {
-        return NULL;
-    }
-    d = (RingBufChardev *)chr;
+    RingBufChardev *d = RINGBUF_CHARDEV(chr);
 
     d->size = opts->has_size ? opts->size : 65536;
 
     /* The size must be power of 2 */
     if (d->size & (d->size - 1)) {
         error_setg(errp, "size of ringbuf chardev must be power of two");
-        goto fail;
+        return;
     }
 
     d->prod = 0;
     d->cons = 0;
     d->cbuf = g_malloc0(d->size);
-
-    return chr;
-
-fail:
-    qemu_chr_free_common(chr);
-    return NULL;
-}
-
-ChardevBackendKind qemu_chr_get_kind(const Chardev *chr)
-{
-    return chr->driver->kind;
 }
 
 void qmp_ringbuf_write(const char *device, const char *data,
@@ -3585,7 +3590,7 @@ void qmp_ringbuf_write(const char *device, const char *data,
         return;
     }
 
-    if (!qemu_chr_is_ringbuf(chr)) {
+    if (!CHARDEV_IS_RINGBUF(chr)) {
         error_setg(errp,"%s is not a ringbuf device", device);
         return;
     }
@@ -3629,7 +3634,7 @@ char *qmp_ringbuf_read(const char *device, int64_t size,
         return NULL;
     }
 
-    if (!qemu_chr_is_ringbuf(chr)) {
+    if (!CHARDEV_IS_RINGBUF(chr)) {
         error_setg(errp,"%s is not a ringbuf device", device);
         return NULL;
     }
@@ -3849,20 +3854,31 @@ static void qemu_chr_parse_stdio(QemuOpts *opts, ChardevBackend *backend,
 static const CharDriver stdio_driver = {
     .kind = CHARDEV_BACKEND_KIND_STDIO,
     .parse = qemu_chr_parse_stdio,
-    .create = qemu_chr_open_stdio,
+};
+
+static void char_stdio_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->open = qemu_chr_open_stdio;
 #ifdef _WIN32
-    sizeof(WinStdioChardev),
-    .chr_write = win_stdio_write,
-    .chr_set_echo = qemu_chr_set_echo_win_stdio,
-    .chr_free = win_stdio_free,
+    cc->chr_write = win_stdio_write;
+    cc->chr_set_echo = qemu_chr_set_echo_win_stdio;
+    cc->chr_free = win_stdio_free;
 #else
-    sizeof(FDChardev),
-    .chr_add_watch = fd_chr_add_watch,
-    .chr_write = fd_chr_write,
-    .chr_update_read_handler = fd_chr_update_read_handler,
-    .chr_set_echo = qemu_chr_set_echo_stdio,
-    .chr_free = qemu_chr_free_stdio,
+    cc->chr_set_echo = qemu_chr_set_echo_stdio;
+    cc->chr_free = qemu_chr_free_stdio;
 #endif
+}
+
+static const TypeInfo char_stdio_type_info = {
+    .name = TYPE_CHARDEV_STDIO,
+#ifdef _WIN32
+    .parent = TYPE_CHARDEV_WIN_STDIO,
+#else
+    .parent = TYPE_CHARDEV_FD,
+#endif
+    .class_init = char_stdio_class_init,
 };
 
 #ifdef HAVE_CHARDEV_SERIAL
@@ -3917,18 +3933,23 @@ static void qemu_chr_parse_pipe(QemuOpts *opts, ChardevBackend *backend,
 static const CharDriver pipe_driver = {
     .kind = CHARDEV_BACKEND_KIND_PIPE,
     .parse = qemu_chr_parse_pipe,
-    .create = qemu_chr_open_pipe,
+};
+
+static void char_pipe_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->open = qemu_chr_open_pipe;
+}
+
+static const TypeInfo char_pipe_type_info = {
+    .name = TYPE_CHARDEV_PIPE,
 #ifdef _WIN32
-    sizeof(WinChardev),
-    .chr_write = win_chr_write,
-    .chr_free = win_chr_free,
+    .parent = TYPE_CHARDEV_WIN,
 #else
-    sizeof(FDChardev),
-    .chr_add_watch = fd_chr_add_watch,
-    .chr_write = fd_chr_write,
-    .chr_update_read_handler = fd_chr_update_read_handler,
-    .chr_free = fd_chr_free,
+    .parent = TYPE_CHARDEV_FD,
 #endif
+    .class_init = char_pipe_class_init,
 };
 
 static void qemu_chr_parse_ringbuf(QemuOpts *opts, ChardevBackend *backend,
@@ -3948,22 +3969,35 @@ static void qemu_chr_parse_ringbuf(QemuOpts *opts, ChardevBackend *backend,
 }
 
 static const CharDriver ringbuf_driver = {
-    .instance_size = sizeof(RingBufChardev),
     .kind = CHARDEV_BACKEND_KIND_RINGBUF,
     .parse = qemu_chr_parse_ringbuf,
-    .create = qemu_chr_open_ringbuf,
-    .chr_write = ringbuf_chr_write,
-    .chr_free = ringbuf_chr_free,
+};
+
+static void char_ringbuf_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->open = qemu_chr_open_ringbuf;
+    cc->chr_write = ringbuf_chr_write;
+    cc->chr_free = ringbuf_chr_free;
+}
+
+static const TypeInfo char_ringbuf_type_info = {
+    .name = TYPE_CHARDEV_RINGBUF,
+    .parent = TYPE_CHARDEV,
+    .class_init = char_ringbuf_class_init,
+    .instance_size = sizeof(RingBufChardev),
 };
 
 /* Bug-compatibility: */
 static const CharDriver memory_driver = {
-    .instance_size = sizeof(RingBufChardev),
     .kind = CHARDEV_BACKEND_KIND_MEMORY,
     .parse = qemu_chr_parse_ringbuf,
-    .create = qemu_chr_open_ringbuf,
-    .chr_write = ringbuf_chr_write,
-    .chr_free = ringbuf_chr_free,
+};
+
+static const TypeInfo char_memory_type_info = {
+    .name = TYPE_CHARDEV_MEMORY,
+    .parent = TYPE_CHARDEV_RINGBUF,
 };
 
 static void qemu_chr_parse_mux(QemuOpts *opts, ChardevBackend *backend,
@@ -3982,14 +4016,26 @@ static void qemu_chr_parse_mux(QemuOpts *opts, ChardevBackend *backend,
 }
 
 static const CharDriver mux_driver = {
-    .instance_size = sizeof(MuxChardev),
     .kind = CHARDEV_BACKEND_KIND_MUX,
     .parse = qemu_chr_parse_mux,
-    .create = qemu_chr_open_mux,
-    .chr_free = mux_chr_free,
-    .chr_write = mux_chr_write,
-    .chr_accept_input = mux_chr_accept_input,
-    .chr_add_watch = mux_chr_add_watch,
+};
+
+static void char_mux_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->open = qemu_chr_open_mux;
+    cc->chr_free = mux_chr_free;
+    cc->chr_write = mux_chr_write;
+    cc->chr_accept_input = mux_chr_accept_input;
+    cc->chr_add_watch = mux_chr_add_watch;
+}
+
+static const TypeInfo char_mux_type_info = {
+    .name = TYPE_CHARDEV_MUX,
+    .parent = TYPE_CHARDEV,
+    .class_init = char_mux_class_init,
+    .instance_size = sizeof(MuxChardev),
 };
 
 static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
@@ -4269,7 +4315,7 @@ Chardev *qemu_chr_new(const char *label, const char *filename)
         if (replay_mode != REPLAY_MODE_NONE) {
             qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_REPLAY);
         }
-        if (qemu_chr_replay(chr) && chr->driver->chr_ioctl) {
+        if (qemu_chr_replay(chr) && CHARDEV_GET_CLASS(chr)->chr_ioctl) {
             error_report("Replay: ioctl is not supported "
                          "for serial devices yet");
         }
@@ -4282,8 +4328,8 @@ void qemu_chr_fe_set_echo(CharBackend *be, bool echo)
 {
     Chardev *chr = be->chr;
 
-    if (chr && chr->driver->chr_set_echo) {
-        chr->driver->chr_set_echo(chr, echo);
+    if (chr && CHARDEV_GET_CLASS(chr)->chr_set_echo) {
+        CHARDEV_GET_CLASS(chr)->chr_set_echo(chr, echo);
     }
 }
 
@@ -4299,8 +4345,8 @@ void qemu_chr_fe_set_open(CharBackend *be, int fe_open)
         return;
     }
     be->fe_open = fe_open;
-    if (chr->driver->chr_set_fe_open) {
-        chr->driver->chr_set_fe_open(chr, fe_open);
+    if (CHARDEV_GET_CLASS(chr)->chr_set_fe_open) {
+        CHARDEV_GET_CLASS(chr)->chr_set_fe_open(chr, fe_open);
     }
 }
 
@@ -4311,11 +4357,11 @@ guint qemu_chr_fe_add_watch(CharBackend *be, GIOCondition cond,
     GSource *src;
     guint tag;
 
-    if (!s || s->driver->chr_add_watch == NULL) {
+    if (!s || CHARDEV_GET_CLASS(s)->chr_add_watch == NULL) {
         return 0;
     }
 
-    src = s->driver->chr_add_watch(s, cond);
+    src = CHARDEV_GET_CLASS(s)->chr_add_watch(s, cond);
     if (!src) {
         return 0;
     }
@@ -4331,31 +4377,17 @@ void qemu_chr_fe_disconnect(CharBackend *be)
 {
     Chardev *chr = be->chr;
 
-    if (chr && chr->driver->chr_disconnect) {
-        chr->driver->chr_disconnect(chr);
+    if (chr && CHARDEV_GET_CLASS(chr)->chr_disconnect) {
+        CHARDEV_GET_CLASS(chr)->chr_disconnect(chr);
     }
 }
 
-static void qemu_chr_free_common(Chardev *chr)
-{
-    if (chr->be) {
-        chr->be->chr = NULL;
-    }
-    g_free(chr->filename);
-    g_free(chr->label);
-    if (chr->logfd != -1) {
-        close(chr->logfd);
-    }
-    qemu_mutex_destroy(&chr->chr_write_lock);
-    g_free(chr);
-}
-
 void qemu_chr_free(Chardev *chr)
 {
-    if (chr->driver->chr_free) {
-        chr->driver->chr_free(chr);
+    if (CHARDEV_GET_CLASS(chr)->chr_free) {
+        CHARDEV_GET_CLASS(chr)->chr_free(chr);
     }
-    qemu_chr_free_common(chr);
+    object_unref(OBJECT(chr));
 }
 
 void qemu_chr_delete(Chardev *chr)
@@ -4523,22 +4555,19 @@ QemuOptsList qemu_chardev_opts = {
 
 #ifdef _WIN32
 
-static Chardev *qmp_chardev_open_file(const CharDriver *driver,
-                                      const char *id,
-                                      ChardevBackend *backend,
-                                      ChardevReturn *ret,
-                                      bool *be_opened,
-                                      Error **errp)
+static void qmp_chardev_open_file(Chardev *chr,
+                                  ChardevBackend *backend,
+                                  bool *be_opened,
+                                  Error **errp)
 {
     ChardevFile *file = backend->u.file.data;
-    ChardevCommon *common = qapi_ChardevFile_base(file);
     HANDLE out;
     DWORD accessmode;
     DWORD flags;
 
     if (file->has_in) {
         error_setg(errp, "input file not supported");
-        return NULL;
+        return;
     }
 
     if (file->has_append && file->append) {
@@ -4555,33 +4584,20 @@ static Chardev *qmp_chardev_open_file(const CharDriver *driver,
                      FILE_ATTRIBUTE_NORMAL, NULL);
     if (out == INVALID_HANDLE_VALUE) {
         error_setg(errp, "open %s failed", file->out);
-        return NULL;
+        return;
     }
-    return qemu_chr_open_win_file(driver, out, common, errp);
+
+    qemu_chr_open_win_file(chr, out);
 }
 
-static Chardev *qmp_chardev_open_serial(const CharDriver *driver,
-                                        const char *id,
-                                        ChardevBackend *backend,
-                                        ChardevReturn *ret,
-                                        bool *be_opened,
-                                        Error **errp)
+static void qmp_chardev_open_serial(Chardev *chr,
+                                    ChardevBackend *backend,
+                                    bool *be_opened,
+                                    Error **errp)
 {
     ChardevHostdev *serial = backend->u.serial.data;
-    ChardevCommon *common = qapi_ChardevHostdev_base(serial);
-    Chardev *chr;
 
-    chr = qemu_chr_alloc(driver, common, errp);
-    if (!chr) {
-        return NULL;
-    }
-
-    if (win_chr_init(chr, serial->device, errp) < 0) {
-        qemu_chr_free_common(chr);
-        return NULL;
-    }
-
-    return chr;
+    win_chr_init(chr, serial->device, errp);
 }
 
 #else /* WIN32 */
@@ -4598,15 +4614,12 @@ static int qmp_chardev_open_file_source(char *src, int flags,
     return fd;
 }
 
-static Chardev *qmp_chardev_open_file(const CharDriver *driver,
-                                      const char *id,
-                                      ChardevBackend *backend,
-                                      ChardevReturn *ret,
-                                      bool *be_opened,
-                                      Error **errp)
+static void qmp_chardev_open_file(Chardev *chr,
+                                  ChardevBackend *backend,
+                                  bool *be_opened,
+                                  Error **errp)
 {
     ChardevFile *file = backend->u.file.data;
-    ChardevCommon *common = qapi_ChardevFile_base(file);
     int flags, in = -1, out;
 
     flags = O_WRONLY | O_CREAT | O_BINARY;
@@ -4618,7 +4631,7 @@ static Chardev *qmp_chardev_open_file(const CharDriver *driver,
 
     out = qmp_chardev_open_file_source(file->out, flags, errp);
     if (out < 0) {
-        return NULL;
+        return;
     }
 
     if (file->has_in) {
@@ -4626,70 +4639,76 @@ static Chardev *qmp_chardev_open_file(const CharDriver *driver,
         in = qmp_chardev_open_file_source(file->in, flags, errp);
         if (in < 0) {
             qemu_close(out);
-            return NULL;
+            return;
         }
     }
 
-    return qemu_chr_open_fd(driver, in, out, common, errp);
+    qemu_chr_open_fd(chr, in, out);
 }
 
 #ifdef HAVE_CHARDEV_SERIAL
-static Chardev *qmp_chardev_open_serial(const CharDriver *driver,
-                                        const char *id,
-                                        ChardevBackend *backend,
-                                        ChardevReturn *ret,
-                                        bool *be_opened,
-                                        Error **errp)
+static void qmp_chardev_open_serial(Chardev *chr,
+                                    ChardevBackend *backend,
+                                    bool *be_opened,
+                                    Error **errp)
 {
     ChardevHostdev *serial = backend->u.serial.data;
-    ChardevCommon *common = qapi_ChardevHostdev_base(serial);
     int fd;
 
     fd = qmp_chardev_open_file_source(serial->device, O_RDWR, errp);
     if (fd < 0) {
-        return NULL;
+        return;
     }
     qemu_set_nonblock(fd);
     tty_serial_init(fd, 115200, 'N', 8, 1);
 
-    return qemu_chr_open_fd(driver, fd, fd, common, errp);
+    qemu_chr_open_fd(chr, fd, fd);
 }
 #endif
 
 #ifdef HAVE_CHARDEV_PARPORT
-static Chardev *qmp_chardev_open_parallel(const CharDriver *driver,
-                                          const char *id,
-                                          ChardevBackend *backend,
-                                          ChardevReturn *ret,
-                                          bool *be_opened,
-                                          Error **errp)
+static void qmp_chardev_open_parallel(Chardev *chr,
+                                      ChardevBackend *backend,
+                                      bool *be_opened,
+                                      Error **errp)
 {
     ChardevHostdev *parallel = backend->u.parallel.data;
-    ChardevCommon *common = qapi_ChardevHostdev_base(parallel);
     int fd;
 
     fd = qmp_chardev_open_file_source(parallel->device, O_RDWR, errp);
     if (fd < 0) {
-        return NULL;
+        return;
     }
-    return qemu_chr_open_pp_fd(driver, fd, common, be_opened, errp);
+    qemu_chr_open_pp_fd(chr, fd, be_opened, errp);
 }
 
 static const CharDriver parallel_driver = {
-    .instance_size = sizeof(ParallelChardev),
     .kind = CHARDEV_BACKEND_KIND_PARALLEL,
     .alias = "parport",
     .parse = qemu_chr_parse_parallel,
-    .create = qmp_chardev_open_parallel,
+};
+
+static void char_parallel_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->open = qmp_chardev_open_parallel;
 #if defined(__linux__)
-    .chr_write = null_chr_write,
-    .chr_ioctl = pp_ioctl,
-    .chr_free = pp_free,
+    cc->chr_write = null_chr_write;
+    cc->chr_ioctl = pp_ioctl;
+    cc->chr_free = pp_free;
 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
-    .chr_write = null_chr_write,
-    .chr_ioctl = pp_ioctl,
     /* FIXME: no chr_free */
+    cc->chr_write = null_chr_write;
+    cc->chr_ioctl = pp_ioctl;
 #endif
+}
+
+static const TypeInfo char_parallel_type_info = {
+    .name = TYPE_CHARDEV_PARALLEL,
+    .parent = TYPE_CHARDEV,
+    .instance_size = sizeof(ParallelChardev),
+    .class_init = char_parallel_class_init,
 };
 #endif
 
@@ -4698,45 +4717,63 @@ static const CharDriver parallel_driver = {
 static const CharDriver file_driver = {
     .kind = CHARDEV_BACKEND_KIND_FILE,
     .parse = qemu_chr_parse_file_out,
-    .create = qmp_chardev_open_file,
+};
+
+static void char_file_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->open = qmp_chardev_open_file;
 #ifdef _WIN32
-    sizeof(WinChardev),
-    .chr_write = win_chr_write,
     /* FIXME: no chr_free */
+    cc->chr_free = NULL;
+#endif
+}
+
+static const TypeInfo char_file_type_info = {
+    .name = TYPE_CHARDEV_FILE,
+#ifdef _WIN32
+    .parent = TYPE_CHARDEV_WIN,
 #else
-    sizeof(FDChardev),
-    .chr_add_watch = fd_chr_add_watch,
-    .chr_write = fd_chr_write,
-    .chr_update_read_handler = fd_chr_update_read_handler,
-    .chr_free = fd_chr_free,
+    .parent = TYPE_CHARDEV_FD,
 #endif
+    .class_init = char_file_class_init,
 };
 
 #ifdef HAVE_CHARDEV_SERIAL
+
 static const CharDriver serial_driver = {
     .kind = CHARDEV_BACKEND_KIND_SERIAL,
     .alias = "tty",
     .parse = qemu_chr_parse_serial,
-    .create = qmp_chardev_open_serial,
+};
+
+static void char_serial_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->open = qmp_chardev_open_serial;
+#ifndef _WIN32
+    cc->chr_ioctl = tty_serial_ioctl;
+    cc->chr_free = qemu_chr_free_tty;
+#endif
+}
+
+static const TypeInfo char_serial_type_info = {
+    .name = TYPE_CHARDEV_SERIAL,
 #ifdef _WIN32
-    sizeof(WinChardev),
-    .chr_write = win_chr_write,
-    .chr_free = win_chr_free,
+    .parent = TYPE_CHARDEV_WIN,
 #else
-    sizeof(FDChardev),
-    .chr_add_watch = fd_chr_add_watch,
-    .chr_write = fd_chr_write,
-    .chr_update_read_handler = fd_chr_update_read_handler,
-    .chr_ioctl = tty_serial_ioctl,
-    .chr_free = qemu_chr_free_tty,
+    .parent = TYPE_CHARDEV_FD,
 #endif
+    .class_init = char_serial_class_init,
 };
 #endif
 
 static gboolean socket_reconnect_timeout(gpointer opaque)
 {
-    Chardev *chr = opaque;
-    SocketChardev *s = opaque;
+    Chardev *chr = CHARDEV(opaque);
+    SocketChardev *s = SOCKET_CHARDEV(opaque);
     QIOChannelSocket *sioc;
 
     s->reconnect_timer = 0;
@@ -4754,15 +4791,12 @@ static gboolean socket_reconnect_timeout(gpointer opaque)
     return false;
 }
 
-static Chardev *qmp_chardev_open_socket(const CharDriver *driver,
-                                        const char *id,
-                                        ChardevBackend *backend,
-                                        ChardevReturn *ret,
-                                        bool *be_opened,
-                                        Error **errp)
+static void qmp_chardev_open_socket(Chardev *chr,
+                                    ChardevBackend *backend,
+                                    bool *be_opened,
+                                    Error **errp)
 {
-    Chardev *chr;
-    SocketChardev *s;
+    SocketChardev *s = SOCKET_CHARDEV(chr);
     ChardevSocket *sock = backend->u.socket.data;
     SocketAddress *addr = sock->addr;
     bool do_nodelay     = sock->has_nodelay ? sock->nodelay : false;
@@ -4770,15 +4804,8 @@ static Chardev *qmp_chardev_open_socket(const CharDriver *driver,
     bool is_telnet      = sock->has_telnet  ? sock->telnet  : false;
     bool is_waitconnect = sock->has_wait    ? sock->wait    : false;
     int64_t reconnect   = sock->has_reconnect ? sock->reconnect : 0;
-    ChardevCommon *common = qapi_ChardevSocket_base(sock);
     QIOChannelSocket *sioc = NULL;
 
-    chr = qemu_chr_alloc(driver, common, errp);
-    if (!chr) {
-        return NULL;
-    }
-    s = (SocketChardev *)chr;
-
     s->is_unix = addr->type == SOCKET_ADDRESS_KIND_UNIX;
     s->is_listen = is_listen;
     s->is_telnet = is_telnet;
@@ -4870,82 +4897,92 @@ static Chardev *qmp_chardev_open_socket(const CharDriver *driver,
         }
     }
 
-    return chr;
+    return;
 
- error:
+error:
     if (sioc) {
         object_unref(OBJECT(sioc));
     }
     if (s->tls_creds) {
         object_unref(OBJECT(s->tls_creds));
     }
-    qemu_chr_free_common(chr);
-    return NULL;
 }
 
 static const CharDriver socket_driver = {
-    .instance_size = sizeof(SocketChardev),
     .kind = CHARDEV_BACKEND_KIND_SOCKET,
     .parse = qemu_chr_parse_socket,
-    .create = qmp_chardev_open_socket,
-    .chr_wait_connected = tcp_chr_wait_connected,
-    .chr_write = tcp_chr_write,
-    .chr_sync_read = tcp_chr_sync_read,
-    .chr_disconnect = tcp_chr_disconnect,
-    .get_msgfds = tcp_get_msgfds,
-    .set_msgfds = tcp_set_msgfds,
-    .chr_add_client = tcp_chr_add_client,
-    .chr_add_watch = tcp_chr_add_watch,
-    .chr_update_read_handler = tcp_chr_update_read_handler,
-    .chr_free = tcp_chr_free,
 };
 
-static Chardev *qmp_chardev_open_udp(const CharDriver *driver,
-                                     const char *id,
-                                     ChardevBackend *backend,
-                                     ChardevReturn *ret,
-                                     bool *be_opened,
-                                     Error **errp)
+static void char_socket_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->open = qmp_chardev_open_socket;
+    cc->chr_wait_connected = tcp_chr_wait_connected;
+    cc->chr_write = tcp_chr_write;
+    cc->chr_sync_read = tcp_chr_sync_read;
+    cc->chr_disconnect = tcp_chr_disconnect;
+    cc->get_msgfds = tcp_get_msgfds;
+    cc->set_msgfds = tcp_set_msgfds;
+    cc->chr_add_client = tcp_chr_add_client;
+    cc->chr_add_watch = tcp_chr_add_watch;
+    cc->chr_update_read_handler = tcp_chr_update_read_handler;
+    cc->chr_free = tcp_chr_free;
+}
+
+static const TypeInfo char_socket_type_info = {
+    .name = TYPE_CHARDEV_SOCKET,
+    .parent = TYPE_CHARDEV,
+    .instance_size = sizeof(SocketChardev),
+    .class_init = char_socket_class_init,
+};
+
+static void qmp_chardev_open_udp(Chardev *chr,
+                                 ChardevBackend *backend,
+                                 bool *be_opened,
+                                 Error **errp)
 {
     ChardevUdp *udp = backend->u.udp.data;
-    ChardevCommon *common = qapi_ChardevUdp_base(udp);
     QIOChannelSocket *sioc = qio_channel_socket_new();
     char *name;
-    Chardev *chr;
-    UdpChardev *s;
+    UdpChardev *s = UDP_CHARDEV(chr);
 
     if (qio_channel_socket_dgram_sync(sioc,
                                       udp->local, udp->remote,
                                       errp) < 0) {
         object_unref(OBJECT(sioc));
-        return NULL;
-    }
-
-    chr = qemu_chr_alloc(driver, common, errp);
-    if (!chr) {
-        return NULL;
+        return;
     }
 
     name = g_strdup_printf("chardev-udp-%s", chr->label);
     qio_channel_set_name(QIO_CHANNEL(sioc), name);
     g_free(name);
 
-    s = (UdpChardev *)chr;
     s->ioc = QIO_CHANNEL(sioc);
     /* be isn't opened until we get a connection */
     *be_opened = false;
-
-    return chr;
 }
 
 static const CharDriver udp_driver = {
-    .instance_size = sizeof(UdpChardev),
     .kind = CHARDEV_BACKEND_KIND_UDP,
     .parse = qemu_chr_parse_udp,
-    .create = qmp_chardev_open_udp,
-    .chr_write = udp_chr_write,
-    .chr_update_read_handler = udp_chr_update_read_handler,
-    .chr_free = udp_chr_free,
+};
+
+static void char_udp_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->open = qmp_chardev_open_udp;
+    cc->chr_write = udp_chr_write;
+    cc->chr_update_read_handler = udp_chr_update_read_handler;
+    cc->chr_free = udp_chr_free;
+}
+
+static const TypeInfo char_udp_type_info = {
+    .name = TYPE_CHARDEV_UDP,
+    .parent = TYPE_CHARDEV,
+    .instance_size = sizeof(UdpChardev),
+    .class_init = char_udp_class_init,
 };
 
 bool qemu_chr_has_feature(Chardev *chr,
@@ -4960,47 +4997,96 @@ void qemu_chr_set_feature(Chardev *chr,
     return set_bit(feature, chr->features);
 }
 
-ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
-                               Error **errp)
+static const ChardevClass *char_get_class(const char *driver, Error **errp)
+{
+    ObjectClass *oc;
+    const ChardevClass *cc;
+    char *typename = g_strdup_printf("chardev-%s", driver);
+
+    oc = object_class_by_name(typename);
+    g_free(typename);
+
+    if (!object_class_dynamic_cast(oc, TYPE_CHARDEV)) {
+        error_setg(errp, "'%s' is not a valid char driver name", driver);
+        return NULL;
+    }
+
+    if (object_class_is_abstract(oc)) {
+        error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "driver",
+                   "abstract device type");
+        return NULL;
+    }
+
+    cc = CHARDEV_CLASS(oc);
+    if (cc->internal) {
+        error_setg(errp, "'%s' is not a valid char driver name", driver);
+        return NULL;
+    }
+
+    return cc;
+}
+
+Chardev *qemu_chardev_new(const char *id, const char *typename,
+                          ChardevBackend *backend, Error **errp)
 {
-    ChardevReturn *ret = g_new0(ChardevReturn, 1);
     Chardev *chr = NULL;
-    const CharDriver *cd;
     Error *local_err = NULL;
     bool be_opened = true;
 
-    chr = qemu_chr_find(id);
-    if (chr) {
-        error_setg(errp, "Chardev '%s' already exists", id);
-        goto out_error;
-    }
+    assert(g_str_has_prefix(typename, "chardev-"));
 
-    cd = (int)backend->type >= 0 && backend->type < ARRAY_SIZE(backends) ?
-        backends[backend->type] : NULL;
-    if (cd == NULL) {
-        error_setg(errp, "chardev backend not available");
-        goto out_error;
-    }
+    chr = CHARDEV(object_new(typename));
+    chr->label = g_strdup(id);
 
-    chr = cd->create(cd, id, backend, ret, &be_opened, &local_err);
+    qemu_char_open(chr, backend, &be_opened, &local_err);
     if (local_err) {
         error_propagate(errp, local_err);
-        goto out_error;
+        object_unref(OBJECT(chr));
+        return NULL;
     }
 
-    chr->label = g_strdup(id);
     if (!chr->filename) {
-        chr->filename = g_strdup(ChardevBackendKind_lookup[backend->type]);
+        chr->filename = g_strdup(typename + 8);
     }
     if (be_opened) {
         qemu_chr_be_event(chr, CHR_EVENT_OPENED);
     }
+
+    return chr;
+}
+
+ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
+                               Error **errp)
+{
+    const ChardevClass *cc;
+    ChardevReturn *ret;
+    Chardev *chr;
+
+    chr = qemu_chr_find(id);
+    if (chr) {
+        error_setg(errp, "Chardev '%s' already exists", id);
+        return NULL;
+    }
+
+    cc = char_get_class(ChardevBackendKind_lookup[backend->type], errp);
+    if (!cc) {
+        return NULL;
+    }
+
+    chr = qemu_chardev_new(id, object_class_get_name(OBJECT_CLASS(cc)),
+                           backend, errp);
+    if (!chr) {
+        return NULL;
+    }
+
+    ret = g_new0(ChardevReturn, 1);
+    if (CHARDEV_IS_PTY(chr)) {
+        ret->pty = g_strdup(chr->filename + 4);
+        ret->has_pty = true;
+    }
+
     QTAILQ_INSERT_TAIL(&chardevs, chr, next);
     return ret;
-
-out_error:
-    g_free(ret);
-    return NULL;
 }
 
 void qmp_chardev_remove(const char *id, Error **errp)
@@ -5035,33 +5121,44 @@ void qemu_chr_cleanup(void)
 
 static void register_types(void)
 {
-    static const CharDriver *drivers[] = {
-        &null_driver,
-        &socket_driver,
-        &udp_driver,
-        &ringbuf_driver,
-        &file_driver,
-        &stdio_driver,
+    static const struct {
+        const CharDriver *driver;
+        const TypeInfo *type;
+    } chardevs[] = {
+        { &null_driver, &char_null_type_info },
+        { &socket_driver, &char_socket_type_info },
+        { &udp_driver, &char_udp_type_info },
+        { &ringbuf_driver, &char_ringbuf_type_info },
+        { &file_driver, &char_file_type_info },
+        { &stdio_driver, &char_stdio_type_info },
 #ifdef HAVE_CHARDEV_SERIAL
-        &serial_driver,
+        { &serial_driver, &char_serial_type_info },
 #endif
 #ifdef HAVE_CHARDEV_PARPORT
-        &parallel_driver,
+        { &parallel_driver, &char_parallel_type_info },
 #endif
 #ifdef HAVE_CHARDEV_PTY
-        &pty_driver,
+        { &pty_driver, &char_pty_type_info },
 #endif
 #ifdef _WIN32
-        &console_driver,
+        { &console_driver, &char_console_type_info },
 #endif
-        &pipe_driver,
-        &mux_driver,
-        &memory_driver
+        { &pipe_driver, &char_pipe_type_info },
+        { &mux_driver, &char_mux_type_info },
+        { &memory_driver, &char_memory_type_info }
     };
     int i;
 
-    for (i = 0; i < ARRAY_SIZE(drivers); i++) {
-        register_char_driver(drivers[i]);
+    type_register_static(&char_type_info);
+#ifndef _WIN32
+    type_register_static(&char_fd_type_info);
+#else
+    type_register_static(&char_win_type_info);
+    type_register_static(&char_win_stdio_type_info);
+#endif
+    for (i = 0; i < ARRAY_SIZE(chardevs); i++) {
+        type_register_static(chardevs[i].type);
+        register_char_driver(chardevs[i].driver);
     }
 
     /* this must be done after machine init, since we register FEs with muxes
diff --git a/spice-qemu-char.c b/spice-qemu-char.c
index 5e5897b..dd97c17 100644
--- a/spice-qemu-char.c
+++ b/spice-qemu-char.c
@@ -18,6 +18,12 @@ typedef struct SpiceChardev {
     QLIST_ENTRY(SpiceChardev) next;
 } SpiceChardev;
 
+#define TYPE_CHARDEV_SPICE "chardev-spice"
+#define TYPE_CHARDEV_SPICEVMC "chardev-spicevmc"
+#define TYPE_CHARDEV_SPICEPORT "chardev-spiceport"
+
+#define SPICE_CHARDEV(obj) OBJECT_CHECK(SpiceChardev, (obj), TYPE_CHARDEV_SPICE)
+
 typedef struct SpiceCharSource {
     GSource               source;
     SpiceChardev       *scd;
@@ -29,7 +35,7 @@ static QLIST_HEAD(, SpiceChardev) spice_chars =
 static int vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len)
 {
     SpiceChardev *scd = container_of(sin, SpiceChardev, sin);
-    Chardev *chr = (Chardev *)scd;
+    Chardev *chr = CHARDEV(scd);
     ssize_t out = 0;
     ssize_t last_out;
     uint8_t* p = (uint8_t*)buf;
@@ -73,7 +79,7 @@ static int vmc_read(SpiceCharDeviceInstance *sin, uint8_t *buf, int len)
 static void vmc_event(SpiceCharDeviceInstance *sin, uint8_t event)
 {
     SpiceChardev *scd = container_of(sin, SpiceChardev, sin);
-    Chardev *chr = (Chardev *)scd;
+    Chardev *chr = CHARDEV(scd);
     int chr_event;
 
     switch (event) {
@@ -92,7 +98,7 @@ static void vmc_event(SpiceCharDeviceInstance *sin, uint8_t event)
 static void vmc_state(SpiceCharDeviceInstance *sin, int connected)
 {
     SpiceChardev *scd = container_of(sin, SpiceChardev, sin);
-    Chardev *chr = (Chardev *)scd;
+    Chardev *chr = CHARDEV(scd);
 
     if ((chr->be_open && connected) ||
         (!chr->be_open && !connected)) {
@@ -173,7 +179,7 @@ static GSourceFuncs SpiceCharSourceFuncs = {
 
 static GSource *spice_chr_add_watch(Chardev *chr, GIOCondition cond)
 {
-    SpiceChardev *scd = (SpiceChardev *)chr;
+    SpiceChardev *scd = SPICE_CHARDEV(chr);
     SpiceCharSource *src;
 
     assert(cond & G_IO_OUT);
@@ -187,7 +193,7 @@ static GSource *spice_chr_add_watch(Chardev *chr, GIOCondition cond)
 
 static int spice_chr_write(Chardev *chr, const uint8_t *buf, int len)
 {
-    SpiceChardev *s = (SpiceChardev *)chr;
+    SpiceChardev *s = SPICE_CHARDEV(chr);
     int read_bytes;
 
     assert(s->datalen == 0);
@@ -206,7 +212,7 @@ static int spice_chr_write(Chardev *chr, const uint8_t *buf, int len)
 
 static void spice_chr_free(struct Chardev *chr)
 {
-    SpiceChardev *s = (SpiceChardev *)chr;
+    SpiceChardev *s = SPICE_CHARDEV(chr);
 
     vmc_unregister_interface(s);
     QLIST_REMOVE(s, next);
@@ -219,7 +225,7 @@ static void spice_chr_free(struct Chardev *chr)
 
 static void spice_vmc_set_fe_open(struct Chardev *chr, int fe_open)
 {
-    SpiceChardev *s = (SpiceChardev *)chr;
+    SpiceChardev *s = SPICE_CHARDEV(chr);
     if (fe_open) {
         vmc_register_interface(s);
     } else {
@@ -230,7 +236,7 @@ static void spice_vmc_set_fe_open(struct Chardev *chr, int fe_open)
 static void spice_port_set_fe_open(struct Chardev *chr, int fe_open)
 {
 #if SPICE_SERVER_VERSION >= 0x000c02
-    SpiceChardev *s = (SpiceChardev *)chr;
+    SpiceChardev *s = SPICE_CHARDEV(chr);
 
     if (fe_open) {
         spice_server_port_event(&s->sin, SPICE_PORT_EVENT_OPENED);
@@ -242,43 +248,29 @@ static void spice_port_set_fe_open(struct Chardev *chr, int fe_open)
 
 static void spice_chr_accept_input(struct Chardev *chr)
 {
-    SpiceChardev *s = (SpiceChardev *)chr;
+    SpiceChardev *s = SPICE_CHARDEV(chr);
 
     spice_server_char_device_wakeup(&s->sin);
 }
 
-static Chardev *chr_open(const CharDriver *driver,
-                                 const char *subtype,
-                                 ChardevCommon *backend,
-                                 Error **errp)
+static void chr_open(Chardev *chr, const char *subtype)
 {
-    Chardev *chr;
-    SpiceChardev *s;
+    SpiceChardev *s = SPICE_CHARDEV(chr);
 
-    chr = qemu_chr_alloc(driver, backend, errp);
-    if (!chr) {
-        return NULL;
-    }
-    s = (SpiceChardev *)chr;
     s->active = false;
     s->sin.subtype = g_strdup(subtype);
 
     QLIST_INSERT_HEAD(&spice_chars, s, next);
-
-    return chr;
 }
 
-static Chardev *qemu_chr_open_spice_vmc(const CharDriver *driver,
-                                                const char *id,
-                                                ChardevBackend *backend,
-                                                ChardevReturn *ret,
-                                                bool *be_opened,
-                                                Error **errp)
+static void qemu_chr_open_spice_vmc(Chardev *chr,
+                                    ChardevBackend *backend,
+                                    bool *be_opened,
+                                    Error **errp)
 {
     ChardevSpiceChannel *spicevmc = backend->u.spicevmc.data;
     const char *type = spicevmc->type;
     const char **psubtype = spice_server_char_device_recognized_subtypes();
-    ChardevCommon *common = qapi_ChardevSpiceChannel_base(spicevmc);
 
     for (; *psubtype != NULL; ++psubtype) {
         if (strcmp(type, *psubtype) == 0) {
@@ -294,41 +286,33 @@ static Chardev *qemu_chr_open_spice_vmc(const CharDriver *driver,
                           subtypes);
 
         g_free(subtypes);
-        return NULL;
+        return;
     }
 
     *be_opened = false;
-    return chr_open(driver, type, common, errp);
+    chr_open(chr, type);
 }
 
 #if SPICE_SERVER_VERSION >= 0x000c02
-static Chardev *qemu_chr_open_spice_port(const CharDriver *driver,
-                                                 const char *id,
-                                                 ChardevBackend *backend,
-                                                 ChardevReturn *ret,
-                                                 bool *be_opened,
-                                                 Error **errp)
+static void qemu_chr_open_spice_port(Chardev *chr,
+                                     ChardevBackend *backend,
+                                     bool *be_opened,
+                                     Error **errp)
 {
     ChardevSpicePort *spiceport = backend->u.spiceport.data;
     const char *name = spiceport->fqdn;
-    ChardevCommon *common = qapi_ChardevSpicePort_base(spiceport);
-    Chardev *chr;
     SpiceChardev *s;
 
     if (name == NULL) {
         error_setg(errp, "missing name parameter");
-        return NULL;
+        return;
     }
 
-    chr = chr_open(driver, "port", common, errp);
-    if (!chr) {
-        return NULL;
-    }
+    chr_open(chr, "port");
+
     *be_opened = false;
-    s = (SpiceChardev *)chr;
+    s = SPICE_CHARDEV(chr);
     s->sin.portname = g_strdup(name);
-
-    return chr;
 }
 
 void qemu_spice_register_ports(void)
@@ -374,32 +358,68 @@ static void qemu_chr_parse_spice_port(QemuOpts *opts, ChardevBackend *backend,
     spiceport->fqdn = g_strdup(name);
 }
 
+static void char_spice_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->chr_write = spice_chr_write;
+    cc->chr_add_watch = spice_chr_add_watch;
+    cc->chr_accept_input = spice_chr_accept_input;
+    cc->chr_free = spice_chr_free;
+}
+
+static const TypeInfo char_spice_type_info = {
+    .name = TYPE_CHARDEV_SPICE,
+    .parent = TYPE_CHARDEV,
+    .instance_size = sizeof(SpiceChardev),
+    .class_init = char_spice_class_init,
+    .abstract = true,
+};
+
+static void char_spicevmc_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->open = qemu_chr_open_spice_vmc;
+    cc->chr_set_fe_open = spice_vmc_set_fe_open;
+}
+
+static const TypeInfo char_spicevmc_type_info = {
+    .name = TYPE_CHARDEV_SPICEVMC,
+    .parent = TYPE_CHARDEV_SPICE,
+    .class_init = char_spicevmc_class_init,
+};
+
+static void char_spiceport_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->open = qemu_chr_open_spice_port;
+    cc->chr_set_fe_open = spice_port_set_fe_open;
+}
+
+static const TypeInfo char_spiceport_type_info = {
+    .name = TYPE_CHARDEV_SPICEPORT,
+    .parent = TYPE_CHARDEV_SPICE,
+    .class_init = char_spiceport_class_init,
+};
+
 static void register_types(void)
 {
     static const CharDriver vmc_driver = {
-        .instance_size = sizeof(SpiceChardev),
         .kind = CHARDEV_BACKEND_KIND_SPICEVMC,
         .parse = qemu_chr_parse_spice_vmc,
-        .create = qemu_chr_open_spice_vmc,
-        .chr_write = spice_chr_write,
-        .chr_add_watch = spice_chr_add_watch,
-        .chr_set_fe_open = spice_vmc_set_fe_open,
-        .chr_accept_input = spice_chr_accept_input,
-        .chr_free = spice_chr_free,
     };
     static const CharDriver port_driver = {
-        .instance_size = sizeof(SpiceChardev),
         .kind = CHARDEV_BACKEND_KIND_SPICEPORT,
         .parse = qemu_chr_parse_spice_port,
-        .create = qemu_chr_open_spice_port,
-        .chr_write = spice_chr_write,
-        .chr_add_watch = spice_chr_add_watch,
-        .chr_set_fe_open = spice_port_set_fe_open,
-        .chr_accept_input = spice_chr_accept_input,
-        .chr_free = spice_chr_free,
     };
     register_char_driver(&vmc_driver);
     register_char_driver(&port_driver);
+
+    type_register_static(&char_spice_type_info);
+    type_register_static(&char_spicevmc_type_info);
+    type_register_static(&char_spiceport_type_info);
 }
 
 type_init(register_types);
diff --git a/ui/console.c b/ui/console.c
index c8ee164..fe03a66 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -1051,9 +1051,12 @@ typedef struct VCChardev {
     QemuConsole *console;
 } VCChardev;
 
+#define TYPE_CHARDEV_VC "chardev-vc"
+#define VC_CHARDEV(obj) OBJECT_CHECK(VCChardev, (obj), TYPE_CHARDEV_VC)
+
 static int vc_chr_write(Chardev *chr, const uint8_t *buf, int len)
 {
-    VCChardev *drv = (VCChardev *)chr;
+    VCChardev *drv = VC_CHARDEV(chr);
     QemuConsole *s = drv->console;
     int i;
 
@@ -1964,7 +1967,7 @@ int qemu_console_get_height(QemuConsole *con, int fallback)
 
 static void vc_chr_set_echo(Chardev *chr, bool echo)
 {
-    VCChardev *drv = (VCChardev *)chr;
+    VCChardev *drv = VC_CHARDEV(chr);
     QemuConsole *s = drv->console;
 
     s->echo = echo;
@@ -2005,7 +2008,7 @@ static const GraphicHwOps text_console_ops = {
 
 static void text_console_do_init(Chardev *chr, DisplayState *ds)
 {
-    VCChardev *drv = (VCChardev *)chr;
+    VCChardev *drv = VC_CHARDEV(chr);
     QemuConsole *s = drv->console;
     int g_width = 80 * FONT_WIDTH;
     int g_height = 24 * FONT_HEIGHT;
@@ -2058,24 +2061,17 @@ static void text_console_do_init(Chardev *chr, DisplayState *ds)
 
 static const CharDriver vc_driver;
 
-static Chardev *vc_chr_init(const CharDriver *driver,
-                            const char *id, ChardevBackend *backend,
-                            ChardevReturn *ret, bool *be_opened,
-                            Error **errp)
+static void vc_chr_open(Chardev *chr,
+                        ChardevBackend *backend,
+                        bool *be_opened,
+                        Error **errp)
 {
     ChardevVC *vc = backend->u.vc.data;
-    ChardevCommon *common = qapi_ChardevVC_base(vc);
-    Chardev *chr;
-    VCChardev *drv;
+    VCChardev *drv = VC_CHARDEV(chr);
     QemuConsole *s;
     unsigned width = 0;
     unsigned height = 0;
 
-    chr = qemu_chr_alloc(&vc_driver, common, errp);
-    if (!chr) {
-        return NULL;
-    }
-
     if (vc->has_width) {
         width = vc->width;
     } else if (vc->has_cols) {
@@ -2097,13 +2093,11 @@ static Chardev *vc_chr_init(const CharDriver *driver,
     }
 
     if (!s) {
-        g_free(chr);
         error_setg(errp, "cannot create text console");
-        return NULL;
+        return;
     }
 
     s->chr = chr;
-    drv = (VCChardev *)chr;
     drv->console = s;
 
     if (display_state) {
@@ -2114,8 +2108,6 @@ static Chardev *vc_chr_init(const CharDriver *driver,
      * stage, so defer OPENED events until they are fully initialized
      */
     *be_opened = false;
-
-    return chr;
 }
 
 void qemu_console_resize(QemuConsole *s, int width, int height)
@@ -2193,19 +2185,39 @@ static const TypeInfo qemu_console_info = {
     .class_size = sizeof(QemuConsoleClass),
 };
 
-static const CharDriver vc_driver = {
+static void char_vc_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->open = vc_chr_open;
+    cc->chr_write = vc_chr_write;
+    cc->chr_set_echo = vc_chr_set_echo;
+}
+
+static const TypeInfo char_vc_type_info = {
+    .name = TYPE_CHARDEV_VC,
+    .parent = TYPE_CHARDEV,
     .instance_size = sizeof(VCChardev),
+    .class_init = char_vc_class_init,
+};
+
+void qemu_console_early_init(void)
+{
+    /* set the default vc driver */
+    if (!object_class_by_name(TYPE_CHARDEV_VC)) {
+        type_register(&char_vc_type_info);
+        register_char_driver(&vc_driver);
+    }
+}
+
+static const CharDriver vc_driver = {
     .kind = CHARDEV_BACKEND_KIND_VC,
     .parse = qemu_chr_parse_vc,
-    .create = vc_chr_init,
-    .chr_write = vc_chr_write,
-    .chr_set_echo = vc_chr_set_echo,
 };
 
 static void register_types(void)
 {
     type_register_static(&qemu_console_info);
-    register_char_driver(&vc_driver);
 }
 
 type_init(register_types);
diff --git a/ui/gtk.c b/ui/gtk.c
index 04df0ad..bdd831c 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -187,6 +187,9 @@ typedef struct VCChardev {
     bool echo;
 } VCChardev;
 
+#define TYPE_CHARDEV_VC "chardev-vc"
+#define VC_CHARDEV(obj) OBJECT_CHECK(VCChardev, (obj), TYPE_CHARDEV_VC)
+
 static void gd_grab_pointer(VirtualConsole *vc, const char *reason);
 static void gd_ungrab_pointer(GtkDisplayState *s);
 static void gd_grab_keyboard(VirtualConsole *vc, const char *reason);
@@ -1691,7 +1694,7 @@ static void gd_vc_adjustment_changed(GtkAdjustment *adjustment, void *opaque)
 
 static int gd_vc_chr_write(Chardev *chr, const uint8_t *buf, int len)
 {
-    VCChardev *vcd = (VCChardev *)chr;
+    VCChardev *vcd = VC_CHARDEV(chr);
     VirtualConsole *vc = vcd->console;
 
     vte_terminal_feed(VTE_TERMINAL(vc->vte.terminal), (const char *)buf, len);
@@ -1700,7 +1703,7 @@ static int gd_vc_chr_write(Chardev *chr, const uint8_t *buf, int len)
 
 static void gd_vc_chr_set_echo(Chardev *chr, bool echo)
 {
-    VCChardev *vcd = (VCChardev *)chr;
+    VCChardev *vcd = VC_CHARDEV(chr);
     VirtualConsole *vc = vcd->console;
 
     if (vc) {
@@ -1714,23 +1717,14 @@ static int nb_vcs;
 static Chardev *vcs[MAX_VCS];
 static const CharDriver gd_vc_driver;
 
-static Chardev *vc_init(const CharDriver *driver,
-                        const char *id, ChardevBackend *backend,
-                        ChardevReturn *ret, bool *be_opened,
-                        Error **errp)
+static void gd_vc_open(Chardev *chr,
+                       ChardevBackend *backend,
+                       bool *be_opened,
+                       Error **errp)
 {
-    ChardevVC *vc = backend->u.vc.data;
-    ChardevCommon *common = qapi_ChardevVC_base(vc);
-    Chardev *chr;
-
     if (nb_vcs == MAX_VCS) {
         error_setg(errp, "Maximum number of consoles reached");
-        return NULL;
-    }
-
-    chr = qemu_chr_alloc(&gd_vc_driver, common, errp);
-    if (!chr) {
-        return NULL;
+        return;
     }
 
     vcs[nb_vcs++] = chr;
@@ -1739,16 +1733,27 @@ static Chardev *vc_init(const CharDriver *driver,
      * stage, so defer OPENED events until they are fully initialized
      */
     *be_opened = false;
+}
 
-    return chr;
+static void char_gd_vc_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->open = gd_vc_open;
+    cc->chr_write = gd_vc_chr_write;
+    cc->chr_set_echo = gd_vc_chr_set_echo;
 }
 
-static const CharDriver gd_vc_driver = {
+static const TypeInfo char_gd_vc_type_info = {
+    .name = TYPE_CHARDEV_VC,
+    .parent = TYPE_CHARDEV,
     .instance_size = sizeof(VCChardev),
+    .class_init = char_gd_vc_class_init,
+};
+
+static const CharDriver gd_vc_driver = {
     .kind = CHARDEV_BACKEND_KIND_VC,
-    .parse = qemu_chr_parse_vc, .create = vc_init,
-    .chr_write = gd_vc_chr_write,
-    .chr_set_echo = gd_vc_chr_set_echo,
+    .parse = qemu_chr_parse_vc,
 };
 
 static gboolean gd_vc_in(VteTerminal *terminal, gchar *text, guint size,
@@ -1786,7 +1791,7 @@ static GSList *gd_vc_vte_init(GtkDisplayState *s, VirtualConsole *vc,
     GtkWidget *box;
     GtkWidget *scrollbar;
     GtkAdjustment *vadjustment;
-    VCChardev *vcd = (VCChardev *)chr;
+    VCChardev *vcd = VC_CHARDEV(chr);
 
     vc->s = s;
     vc->vte.echo = vcd->echo;
@@ -2347,7 +2352,7 @@ void early_gtk_display_init(int opengl)
     }
 
 #if defined(CONFIG_VTE)
-    /* overwrite the console.c vc driver */
+    type_register(&char_gd_vc_type_info);
     register_char_driver(&gd_vc_driver);
 #endif
 }
diff --git a/vl.c b/vl.c
index 41be59f..f788275 100644
--- a/vl.c
+++ b/vl.c
@@ -4238,6 +4238,8 @@ int main(int argc, char **argv, char **envp)
         sdl_display_early_init(request_opengl);
     }
 
+    qemu_console_early_init();
+
     if (request_opengl == 1 && display_opengl == 0) {
 #if defined(CONFIG_OPENGL)
         error_report("OpenGL is not supported by the display");
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [Qemu-devel] [PULL 41/41] memory: don't sign-extend 32-bit writes
  2017-01-27 13:45 [Qemu-devel] [PULL 00/41] Misc changes for 2017-01-27 Paolo Bonzini
                   ` (39 preceding siblings ...)
  2017-01-27 13:45 ` [Qemu-devel] [PULL 40/41] chardev: qom-ify Paolo Bonzini
@ 2017-01-27 13:45 ` Paolo Bonzini
  2017-01-27 16:58 ` [Qemu-devel] [PULL 00/41] Misc changes for 2017-01-27 Peter Maydell
  41 siblings, 0 replies; 43+ messages in thread
From: Paolo Bonzini @ 2017-01-27 13:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: Ladi Prosek

From: Ladi Prosek <lprosek@redhat.com>

ldl_p has a signed return type so assigning it to uint64_t implicitly
sign-extends the value. This results in devices with min_access_size = 8
seeing unexpected values passed to their write handlers.

Example: guest performs a 32-bit write of 0x80000000 to an mmio region
and the handler receives 0xFFFFFFFF80000000 in its value argument.

Signed-off-by: Ladi Prosek <lprosek@redhat.com>
Message-Id: <1485440557-10384-1-git-send-email-lprosek@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 exec.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/exec.c b/exec.c
index f2bed92..b05c5d2 100644
--- a/exec.c
+++ b/exec.c
@@ -2630,7 +2630,7 @@ static MemTxResult address_space_write_continue(AddressSpace *as, hwaddr addr,
                 break;
             case 4:
                 /* 32 bit write access */
-                val = ldl_p(buf);
+                val = (uint32_t)ldl_p(buf);
                 result |= memory_region_dispatch_write(mr, addr1, val, 4,
                                                        attrs);
                 break;
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* Re: [Qemu-devel] [PULL 00/41] Misc changes for 2017-01-27
  2017-01-27 13:45 [Qemu-devel] [PULL 00/41] Misc changes for 2017-01-27 Paolo Bonzini
                   ` (40 preceding siblings ...)
  2017-01-27 13:45 ` [Qemu-devel] [PULL 41/41] memory: don't sign-extend 32-bit writes Paolo Bonzini
@ 2017-01-27 16:58 ` Peter Maydell
  41 siblings, 0 replies; 43+ messages in thread
From: Peter Maydell @ 2017-01-27 16:58 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: QEMU Developers

On 27 January 2017 at 13:45, Paolo Bonzini <pbonzini@redhat.com> wrote:
> The following changes since commit 3879284d6517dc22529395bdb259f4183b589127:
>
>   Merge remote-tracking branch 'remotes/berrange/tags/pull-qio-2017-01-23-2' into staging (2017-01-23 15:59:09 +0000)
>
> are available in the git repository at:
>
>
>   git://github.com/bonzini/qemu.git tags/for-upstream
>
> for you to fetch changes up to 0a24326232f7c7f8c5ab9080784312d05d496e47:
>
>   memory: don't sign-extend 32-bit writes (2017-01-26 15:29:49 +0100)
>

Conflict in target/i386/kvm.c which I didn't quite feel
confident of resolving, though I suspect it's mostly
trivial. Can you rebase, please?

thanks
-- PMM

^ permalink raw reply	[flat|nested] 43+ messages in thread

end of thread, other threads:[~2017-01-27 16:59 UTC | newest]

Thread overview: 43+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-01-27 13:45 [Qemu-devel] [PULL 00/41] Misc changes for 2017-01-27 Paolo Bonzini
2017-01-27 13:45 ` [Qemu-devel] [PULL 01/41] icount: update instruction counter on apic patching Paolo Bonzini
2017-01-27 13:45 ` [Qemu-devel] [PULL 02/41] replay: improve interrupt handling Paolo Bonzini
2017-01-27 13:45 ` [Qemu-devel] [PULL 03/41] replay: don't use rtc clock on loadvm phase Paolo Bonzini
2017-01-27 13:45 ` [Qemu-devel] [PULL 04/41] savevm: add public save_vmstate function Paolo Bonzini
2017-01-27 13:45 ` [Qemu-devel] [PULL 05/41] replay: save/load initial state Paolo Bonzini
2017-01-27 13:45 ` [Qemu-devel] [PULL 06/41] replay: exception replay fix Paolo Bonzini
2017-01-27 13:45 ` [Qemu-devel] [PULL 07/41] apic: save apic_delivered flag Paolo Bonzini
2017-01-27 13:45 ` [Qemu-devel] [PULL 08/41] memory: tune mtree_print_mr() to dump mr type Paolo Bonzini
2017-01-27 13:45 ` [Qemu-devel] [PULL 09/41] memory: hmp: add "-f" for "info mtree" Paolo Bonzini
2017-01-27 13:45 ` [Qemu-devel] [PULL 10/41] hw/isa/lpc_ich9: add SMI feature negotiation via fw_cfg Paolo Bonzini
2017-01-27 13:45 ` [Qemu-devel] [PULL 11/41] hw/isa/lpc_ich9: add broadcast SMI feature Paolo Bonzini
2017-01-27 13:45 ` [Qemu-devel] [PULL 12/41] hw/isa/lpc_ich9: negotiate SMI broadcast on pc-q35-2.9+ machine types Paolo Bonzini
2017-01-27 13:45 ` [Qemu-devel] [PULL 13/41] block/iscsi: avoid data corruption with cache=writeback Paolo Bonzini
2017-01-27 13:45 ` [Qemu-devel] [PULL 14/41] Introduce DEVICE_CATEGORY_CPU for CPU devices Paolo Bonzini
2017-01-27 13:45 ` [Qemu-devel] [PULL 15/41] hw/scsi: Fix debug message of cdb structure in scsi-generic Paolo Bonzini
2017-01-27 13:45 ` [Qemu-devel] [PULL 16/41] block: Fix target variable of BLKSECTGET ioctl Paolo Bonzini
2017-01-27 13:45 ` [Qemu-devel] [PULL 17/41] block: get max_transfer limit for char (scsi-generic) devices Paolo Bonzini
2017-01-27 13:45 ` [Qemu-devel] [PULL 18/41] x86-KVM: Supply TSC and APIC clock rates to guest like VMWare Paolo Bonzini
2017-01-27 13:45 ` [Qemu-devel] [PULL 19/41] pc: Enable vmware-cpuid-freq CPU option for 2.9+ machine types Paolo Bonzini
2017-01-27 13:45 ` [Qemu-devel] [PULL 20/41] block/iscsi: statically link qemu_iscsi_opts Paolo Bonzini
2017-01-27 13:45 ` [Qemu-devel] [PULL 21/41] tests: fix linking test-char on win32 Paolo Bonzini
2017-01-27 13:45 ` [Qemu-devel] [PULL 22/41] qemu-options: stdio is available " Paolo Bonzini
2017-01-27 13:45 ` [Qemu-devel] [PULL 23/41] char: add qemu_chr_fe_add_watch() Returns description Paolo Bonzini
2017-01-27 13:45 ` [Qemu-devel] [PULL 24/41] doc: fix spelling Paolo Bonzini
2017-01-27 13:45 ` [Qemu-devel] [PULL 25/41] char: use a const CharDriver Paolo Bonzini
2017-01-27 13:45 ` [Qemu-devel] [PULL 26/41] char: use a static array for backends Paolo Bonzini
2017-01-27 13:45 ` [Qemu-devel] [PULL 27/41] char: move callbacks in CharDriver Paolo Bonzini
2017-01-27 13:45 ` [Qemu-devel] [PULL 28/41] char: fold single-user functions in caller Paolo Bonzini
2017-01-27 13:45 ` [Qemu-devel] [PULL 29/41] char: introduce generic qemu_chr_get_kind() Paolo Bonzini
2017-01-27 13:45 ` [Qemu-devel] [PULL 30/41] char: use a feature bit for replay Paolo Bonzini
2017-01-27 13:45 ` [Qemu-devel] [PULL 31/41] char: allocate CharDriverState as a single object Paolo Bonzini
2017-01-27 13:45 ` [Qemu-devel] [PULL 32/41] bt: use qemu_chr_alloc() Paolo Bonzini
2017-01-27 13:45 ` [Qemu-devel] [PULL 33/41] char: rename CharDriverState Chardev Paolo Bonzini
2017-01-27 13:45 ` [Qemu-devel] [PULL 34/41] char: rename TCPChardev and NetChardev Paolo Bonzini
2017-01-27 13:45 ` [Qemu-devel] [PULL 35/41] spice-char: improve error reporting Paolo Bonzini
2017-01-27 13:45 ` [Qemu-devel] [PULL 36/41] char: use error_report() Paolo Bonzini
2017-01-27 13:45 ` [Qemu-devel] [PULL 37/41] gtk: overwrite the console.c char driver Paolo Bonzini
2017-01-27 13:45 ` [Qemu-devel] [PULL 38/41] baum: use a common prefix for chr callbacks Paolo Bonzini
2017-01-27 13:45 ` [Qemu-devel] [PULL 39/41] vc: " Paolo Bonzini
2017-01-27 13:45 ` [Qemu-devel] [PULL 40/41] chardev: qom-ify Paolo Bonzini
2017-01-27 13:45 ` [Qemu-devel] [PULL 41/41] memory: don't sign-extend 32-bit writes Paolo Bonzini
2017-01-27 16:58 ` [Qemu-devel] [PULL 00/41] Misc changes for 2017-01-27 Peter Maydell

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.