summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2017-01-30 10:23:20 +0000
committerPeter Maydell <peter.maydell@linaro.org>2017-01-30 10:23:20 +0000
commita0def594286d9110a6035e02eef558cf3cf5d847 (patch)
tree32275a1bfdd6c17682788f2b2e018a15c3b30214
parent3aca12f841fcd6f3a7477076dad0d564360500de (diff)
parent6da67de6803e93cbb7e93ac3497865832f8c00ea (diff)
downloadfocaccia-qemu-a0def594286d9110a6035e02eef558cf3cf5d847.tar.gz
focaccia-qemu-a0def594286d9110a6035e02eef558cf3cf5d847.zip
Merge remote-tracking branch 'remotes/bonzini/tags/for-upstream' into staging
* 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)

# gpg: Signature made Fri 27 Jan 2017 17:08:51 GMT
# gpg:                using RSA key 0xBFFBD25F78C7AE83
# gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>"
# gpg:                 aka "Paolo Bonzini <pbonzini@redhat.com>"
# Primary key fingerprint: 46F5 9FBD 57D6 12E7 BFD4  E2F7 7E15 100C CD36 69B1
#      Subkey fingerprint: F133 3857 4B66 2389 866C  7682 BFFB D25F 78C7 AE83

* remotes/bonzini/tags/for-upstream: (41 commits)
  memory: don't sign-extend 32-bit writes
  chardev: qom-ify
  vc: use a common prefix for chr callbacks
  baum: use a common prefix for chr callbacks
  gtk: overwrite the console.c char driver
  char: use error_report()
  spice-char: improve error reporting
  char: rename TCPChardev and NetChardev
  char: rename CharDriverState Chardev
  bt: use qemu_chr_alloc()
  char: allocate CharDriverState as a single object
  char: use a feature bit for replay
  char: introduce generic qemu_chr_get_kind()
  char: fold single-user functions in caller
  char: move callbacks in CharDriver
  char: use a static array for backends
  char: use a const CharDriver
  doc: fix spelling
  char: add qemu_chr_fe_add_watch() Returns description
  qemu-options: stdio is available on win32
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r--MAINTAINERS1
-rw-r--r--backends/baum.c102
-rw-r--r--backends/msmouse.c77
-rw-r--r--backends/rng-egd.c4
-rw-r--r--backends/testdev.c53
-rw-r--r--block/Makefile.objs1
-rw-r--r--block/file-posix.c19
-rw-r--r--block/iscsi-opts.c69
-rw-r--r--block/iscsi.c8
-rw-r--r--cpu-exec.c2
-rw-r--r--docs/replay.txt16
-rw-r--r--exec.c2
-rw-r--r--gdbstub.c45
-rw-r--r--hmp-commands-info.hx6
-rw-r--r--hw/arm/fsl-imx25.c2
-rw-r--r--hw/arm/fsl-imx31.c2
-rw-r--r--hw/arm/fsl-imx6.c2
-rw-r--r--hw/arm/nseries.c2
-rw-r--r--hw/arm/omap2.c2
-rw-r--r--hw/arm/pxa2xx.c2
-rw-r--r--hw/arm/virt.c2
-rw-r--r--hw/bt/hci-csr.c64
-rw-r--r--hw/char/escc.c2
-rw-r--r--hw/char/exynos4210_uart.c2
-rw-r--r--hw/char/imx_serial.c2
-rw-r--r--hw/char/mcf_uart.c4
-rw-r--r--hw/char/omap_uart.c6
-rw-r--r--hw/char/parallel.c2
-rw-r--r--hw/char/serial-isa.c2
-rw-r--r--hw/char/serial.c4
-rw-r--r--hw/char/sh_serial.c2
-rw-r--r--hw/char/spapr_vty.c2
-rw-r--r--hw/char/virtio-console.c2
-rw-r--r--hw/core/qdev-properties-system.c4
-rw-r--r--hw/cpu/core.c8
-rw-r--r--hw/display/milkymist-tmu2.c2
-rw-r--r--hw/display/sm501.c2
-rw-r--r--hw/i386/kvmvapic.c6
-rw-r--r--hw/intc/apic_common.c33
-rw-r--r--hw/isa/isa-bus.c2
-rw-r--r--hw/isa/lpc_ich9.c91
-rw-r--r--hw/isa/pc87312.c2
-rw-r--r--hw/lm32/lm32.h4
-rw-r--r--hw/lm32/milkymist-hw.h2
-rw-r--r--hw/mips/mips_malta.c4
-rw-r--r--hw/misc/ivshmem.c2
-rw-r--r--hw/misc/milkymist-pfpu.c2
-rw-r--r--hw/scsi/scsi-generic.c5
-rw-r--r--hw/timer/mc146818rtc.c15
-rw-r--r--hw/usb/ccid-card-passthru.c2
-rw-r--r--hw/usb/dev-serial.c6
-rw-r--r--hw/usb/redirect.c4
-rw-r--r--include/block/block.h1
-rw-r--r--include/exec/memory.h2
-rw-r--r--include/hw/arm/exynos4210.h2
-rw-r--r--include/hw/arm/omap.h6
-rw-r--r--include/hw/bt.h4
-rw-r--r--include/hw/char/cadence_uart.h2
-rw-r--r--include/hw/char/escc.h2
-rw-r--r--include/hw/char/pl011.h4
-rw-r--r--include/hw/char/serial.h4
-rw-r--r--include/hw/char/xilinx_uartlite.h2
-rw-r--r--include/hw/cris/etraxfs.h2
-rw-r--r--include/hw/devices.h2
-rw-r--r--include/hw/i386/apic_internal.h2
-rw-r--r--include/hw/i386/ich9.h13
-rw-r--r--include/hw/i386/pc.h12
-rw-r--r--include/hw/m68k/mcf.h4
-rw-r--r--include/hw/ppc/spapr_vio.h2
-rw-r--r--include/hw/qdev-core.h1
-rw-r--r--include/hw/qdev-properties.h2
-rw-r--r--include/hw/sh4/sh.h2
-rw-r--r--include/hw/sparc/grlib.h2
-rw-r--r--include/hw/xen/xen.h2
-rw-r--r--include/monitor/monitor.h2
-rw-r--r--include/qemu/typedefs.h2
-rw-r--r--include/sysemu/char.h155
-rw-r--r--include/sysemu/replay.h13
-rw-r--r--include/sysemu/sysemu.h5
-rw-r--r--include/ui/console.h2
-rw-r--r--include/ui/gtk.h2
-rw-r--r--include/ui/qemu-spice.h2
-rw-r--r--memory.c89
-rw-r--r--migration/savevm.c33
-rw-r--r--monitor.c10
-rw-r--r--net/colo-compare.c4
-rw-r--r--net/filter-mirror.c4
-rw-r--r--net/slirp.c2
-rw-r--r--net/vhost-user.c10
-rw-r--r--qdev-monitor.c1
-rw-r--r--qemu-char.c1914
-rw-r--r--qemu-options.hx12
-rw-r--r--qmp.c2
-rw-r--r--qom/cpu.c1
-rw-r--r--qtest.c2
-rwxr-xr-xreplay/replay-char.c8
-rw-r--r--replay/replay-snapshot.c17
-rw-r--r--replay/replay.c5
-rw-r--r--spice-qemu-char.c231
-rw-r--r--stubs/monitor.c2
-rw-r--r--stubs/replay.c4
-rw-r--r--target/i386/cpu.c1
-rw-r--r--target/i386/cpu.h4
-rw-r--r--target/i386/kvm.c36
-rw-r--r--target/i386/seg_helper.c1
-rw-r--r--tests/Makefile.include2
-rw-r--r--tests/test-char.c10
-rw-r--r--tests/vhost-user-test.c4
-rw-r--r--translate-all.c2
-rw-r--r--ui/console.c111
-rw-r--r--ui/gtk.c86
-rw-r--r--vl.c59
-rw-r--r--xen-common-stub.c2
-rw-r--r--xen-common.c4
114 files changed, 2192 insertions, 1448 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index a428cb2e23..e0be7bc0d4 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1642,6 +1642,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/backends/baum.c b/backends/baum.c
index b045ef49c5..0f418ed358 100644
--- a/backends/baum.c
+++ b/backends/baum.c
@@ -85,7 +85,7 @@
 #define BUF_SIZE 256
 
 typedef struct {
-    CharDriverState *chr;
+    Chardev parent;
 
     brlapi_handle_t *brlapi;
     int brlapi_fd;
@@ -98,7 +98,10 @@ typedef struct {
     uint8_t out_buf_used, out_buf_ptr;
 
     QEMUTimer *cellCount_timer;
-} BaumDriverState;
+} 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 {
@@ -223,7 +226,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 +256,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_chr_accept_input(struct Chardev *chr)
 {
-    BaumDriverState *baum = chr->opaque;
+    BaumChardev *baum = BAUM_CHARDEV(chr);
     int room, first;
 
     if (!baum->out_buf_used)
@@ -279,24 +282,25 @@ 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)
 {
+    Chardev *chr = CHARDEV(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) {
@@ -321,14 +325,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 = 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));
 }
 
 /* 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;
@@ -469,9 +473,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_chr_write(Chardev *chr, const uint8_t *buf, int len)
 {
-    BaumDriverState *baum = chr->opaque;
+    BaumChardev *baum = BAUM_CHARDEV(chr);
     int tocopy, cur, eaten, orig_len = len;
 
     if (!len)
@@ -510,14 +514,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));
@@ -526,7 +532,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 = BAUM_CHARDEV(opaque);
     brlapi_keyCode_t code;
     int ret;
     if (!baum->brlapi)
@@ -610,41 +616,25 @@ static void baum_chr_read(void *opaque)
     }
 }
 
-static void baum_free(struct CharDriverState *chr)
+static void baum_chr_free(Chardev *chr)
 {
-    BaumDriverState *baum = chr->opaque;
+    BaumChardev *baum = BAUM_CHARDEV(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 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;
-    BaumDriverState *baum;
-    CharDriverState *chr;
+    BaumChardev *baum = BAUM_CHARDEV(chr);
     brlapi_handle_t *handle;
 
-    chr = qemu_chr_alloc(common, errp);
-    if (!chr) {
-        return NULL;
-    }
-    baum = g_malloc0(sizeof(BaumDriverState));
-    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;
 
@@ -652,27 +642,41 @@ static CharDriverState *chr_baum_init(const char *id,
     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);
-    g_free(baum);
-    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)
 {
-    register_char_driver("braille", CHARDEV_BACKEND_KIND_BRAILLE, NULL,
-                         chr_baum_init);
+    static const CharDriver driver = {
+        .kind = CHARDEV_BACKEND_KIND_BRAILLE,
+    };
+
+    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 733ca80f48..936a5476d5 100644
--- a/backends/msmouse.c
+++ b/backends/msmouse.c
@@ -31,18 +31,23 @@
 #define MSMOUSE_HI2(n) (((n) & 0xc0) >> 6)
 
 typedef struct {
-    CharDriverState *chr;
+    Chardev parent;
+
     QemuInputHandlerState *hs;
     int axis[INPUT_AXIS__MAX];
     bool btns[INPUT_BUTTON__MAX];
     bool btnc[INPUT_BUTTON__MAX];
     uint8_t outbuf[32];
     int outlen;
-} MouseState;
+} MouseChardev;
+
+#define TYPE_CHARDEV_MSMOUSE "chardev-msmouse"
+#define MOUSE_CHARDEV(obj)                                      \
+    OBJECT_CHECK(MouseChardev, (obj), TYPE_CHARDEV_MSMOUSE)
 
-static void msmouse_chr_accept_input(CharDriverState *chr)
+static void msmouse_chr_accept_input(Chardev *chr)
 {
-    MouseState *mouse = chr->opaque;
+    MouseChardev *mouse = MOUSE_CHARDEV(chr);
     int len;
 
     len = qemu_chr_be_can_write(chr);
@@ -60,7 +65,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;
@@ -97,7 +102,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 = MOUSE_CHARDEV(dev);
     InputMoveEvent *move;
     InputBtnEvent *btn;
 
@@ -121,24 +126,24 @@ static void msmouse_input_event(DeviceState *dev, QemuConsole *src,
 
 static void msmouse_input_sync(DeviceState *dev)
 {
-    MouseState *mouse = (MouseState *)dev;
+    MouseChardev *mouse = MOUSE_CHARDEV(dev);
+    Chardev *chr = CHARDEV(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)
+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 = chr->opaque;
+    MouseChardev *mouse = MOUSE_CHARDEV(chr);
 
     qemu_input_handler_unregister(mouse->hs);
-    g_free(mouse);
 }
 
 static QemuInputHandler msmouse_handler = {
@@ -148,39 +153,43 @@ static QemuInputHandler msmouse_handler = {
     .sync  = msmouse_input_sync,
 };
 
-static CharDriverState *qemu_chr_open_msmouse(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;
-    MouseState *mouse;
-    CharDriverState *chr;
+    MouseChardev *mouse = MOUSE_CHARDEV(chr);
 
-    chr = qemu_chr_alloc(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);
     mouse->hs = qemu_input_handler_register((DeviceState *)mouse,
                                             &msmouse_handler);
+}
 
-    mouse->chr = chr;
-    chr->opaque = mouse;
+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)
 {
-    register_char_driver("msmouse", CHARDEV_BACKEND_KIND_MSMOUSE, NULL,
-                         qemu_chr_open_msmouse);
+    static const CharDriver driver = {
+        .kind = CHARDEV_BACKEND_KIND_MSMOUSE,
+    };
+
+    register_char_driver(&driver);
+    type_register_static(&char_msmouse_type_info);
 }
 
 type_init(register_types);
diff --git a/backends/rng-egd.c b/backends/rng-egd.c
index 69c04b1b26..380b19a0a1 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 60156e320e..ea15143713 100644
--- a/backends/testdev.c
+++ b/backends/testdev.c
@@ -30,13 +30,18 @@
 #define BUF_SIZE 32
 
 typedef struct {
-    CharDriverState *chr;
+    Chardev parent;
+
     uint8_t in_buf[32];
     int in_buf_used;
-} TestdevCharState;
+} 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(TestdevCharState *testdev)
+static int testdev_eat_packet(TestdevChardev *testdev)
 {
     const uint8_t *cur = testdev->in_buf;
     int len = testdev->in_buf_used;
@@ -77,9 +82,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_chr_write(Chardev *chr, const uint8_t *buf, int len)
 {
-    TestdevCharState *testdev = chr->opaque;
+    TestdevChardev *testdev = TESTDEV_CHARDEV(chr);
     int tocopy, eaten, orig_len = len;
 
     while (len) {
@@ -102,36 +107,28 @@ static int testdev_write(CharDriverState *chr, const uint8_t *buf, int len)
     return orig_len;
 }
 
-static void testdev_free(struct CharDriverState *chr)
+static void char_testdev_class_init(ObjectClass *oc, void *data)
 {
-    TestdevCharState *testdev = chr->opaque;
+    ChardevClass *cc = CHARDEV_CLASS(oc);
 
-    g_free(testdev);
+    cc->chr_write = testdev_chr_write;
 }
 
-static CharDriverState *chr_testdev_init(const char *id,
-                                         ChardevBackend *backend,
-                                         ChardevReturn *ret,
-                                         bool *be_opened,
-                                         Error **errp)
-{
-    TestdevCharState *testdev;
-    CharDriverState *chr;
-
-    testdev = g_new0(TestdevCharState, 1);
-    testdev->chr = chr = g_new0(CharDriverState, 1);
-
-    chr->opaque = testdev;
-    chr->chr_write = testdev_write;
-    chr->chr_free = testdev_free;
-
-    return chr;
-}
+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)
 {
-    register_char_driver("testdev", CHARDEV_BACKEND_KIND_TESTDEV, NULL,
-                         chr_testdev_init);
+    static const CharDriver driver = {
+        .kind = CHARDEV_BACKEND_KIND_TESTDEV,
+    };
+
+    register_char_driver(&driver);
+    type_register_static(&char_testdev_type_info);
 }
 
 type_init(register_types);
diff --git a/block/Makefile.objs b/block/Makefile.objs
index 0b8fd06f27..c6bd14e883 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/file-posix.c b/block/file-posix.c
index 28b47d977b..2134e0ef96 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;
     }
@@ -671,10 +674,10 @@ static void raw_refresh_limits(BlockDriverState *bs, Error **errp)
     struct stat st;
 
     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);
+        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);
             }
         }
     }
diff --git a/block/iscsi-opts.c b/block/iscsi-opts.c
new file mode 100644
index 0000000000..5335539130
--- /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/block/iscsi.c b/block/iscsi.c
index 6aeeb9ec4f..1860f1bc91 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);
diff --git a/cpu-exec.c b/cpu-exec.c
index 4188fed3c6..fa08c733da 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/docs/replay.txt b/docs/replay.txt
index 347b2ff055..03e193193f 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/exec.c b/exec.c
index f2bed92b64..b05c5d2d74 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;
diff --git a/gdbstub.c b/gdbstub.c
index 2d18ed73be..755a8e378d 100644
--- a/gdbstub.c
+++ b/gdbstub.c
@@ -305,7 +305,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;
@@ -1473,7 +1473,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;
@@ -1698,7 +1698,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;
@@ -1725,13 +1725,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];
-    CharDriverState *chr = NULL;
-    CharDriverState *mon_chr;
-    ChardevCommon common = { 0 };
+    Chardev *chr = NULL;
+    Chardev *mon_chr;
 
     if (!first_cpu) {
         error_report("gdbstub: meaningless to attach gdb to a "
@@ -1770,8 +1792,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(&common, &error_abort);
-        mon_chr->chr_write = gdb_monitor_write;
+        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)) {
@@ -1794,4 +1816,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/hmp-commands-info.hx b/hmp-commands-info.hx
index 55d50c42c6..b0f35e6829 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/hw/arm/fsl-imx25.c b/hw/arm/fsl-imx25.c
index 7bb7be76b6..2126f73ca0 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 f23672b222..dd1c713ae3 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 e93532fb57..76dd8a48ca 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 c86cf80514..503a3b6d24 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 6f05c98d3e..cf1b4ba58f 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 d31b4577f0..cfee3929d9 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 6c9e8985bf..1f216cf3b1 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 fbb3109cc1..3c193848fc 100644
--- a/hw/bt/hci-csr.c
+++ b/hw/bt/hci-csr.c
@@ -26,13 +26,14 @@
 #include "hw/irq.h"
 #include "sysemu/bt.h"
 #include "hw/bt.h"
+#include "qapi/error.h"
 
 struct csrhci_s {
+    Chardev parent;
     int enable;
     qemu_irq *pins;
     int pin_state;
     int modem_state;
-    CharDriverState chr;
 #define FIFO_LEN	4096
     int out_start;
     int out_len;
@@ -54,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,
@@ -78,7 +82,8 @@ enum {
 
 static inline void csrhci_fifo_wake(struct csrhci_s *s)
 {
-    CharBackend *be = s->chr.be;
+    Chardev *chr = (Chardev *)s;
+    CharBackend *be = chr->be;
 
     if (!s->enable || !s->out_len)
         return;
@@ -311,10 +316,10 @@ 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->opaque;
+    struct csrhci_s *s = (struct csrhci_s *)chr;
     int total = 0;
 
     if (!s->enable)
@@ -384,10 +389,10 @@ 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->opaque;
+    struct csrhci_s *s = (struct csrhci_s *) chr;
     int prev_state = s->modem_state;
 
     switch (cmd) {
@@ -453,21 +458,19 @@ 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->opaque;
+    struct csrhci_s *s = (struct csrhci_s *) chr;
 
     return s->pins;
 }
 
-CharDriverState *uart_hci_init(void)
+static void csrhci_open(Chardev *chr,
+                        ChardevBackend *backend,
+                        bool *be_opened,
+                        Error **errp)
 {
-    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;
+    struct csrhci_s *s = HCI_CHARDEV(chr);
 
     s->hci = qemu_next_hci();
     s->hci->opaque = s;
@@ -477,6 +480,35 @@ CharDriverState *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;
+}
 
-    return &s->chr;
+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,
+};
+
+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/hw/char/escc.c b/hw/char/escc.c
index d6662dc77d..9228091cec 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 0cd3dd3958..7c16e894e2 100644
--- a/hw/char/exynos4210_uart.c
+++ b/hw/char/exynos4210_uart.c
@@ -582,7 +582,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 99545fc359..52e67f8dc9 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 ecaa091190..80c380e077 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 893ab108bc..31ebb1592c 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 f2d56666b7..c71a4a0f4f 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 54d3a12f51..d7c5cc11fe 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 67b18eda12..03d890ca24 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 9d35564bcf..303eb0a678 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 7c22b8bd0e..e30c8da57c 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 776205b4a9..798d9b69fd 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 1b7ea50e9f..94f4d8bde4 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/cpu/core.c b/hw/cpu/core.c
index eff90c12be..2bf960d6a8 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/hw/display/milkymist-tmu2.c b/hw/display/milkymist-tmu2.c
index 920374b985..7528665510 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 5f71012108..040a0b93f2 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/i386/kvmvapic.c b/hw/i386/kvmvapic.c
index 702e281dc8..7135633863 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();
diff --git a/hw/intc/apic_common.c b/hw/intc/apic_common.c
index 3945dfd7b9..17df24c9d0 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/hw/isa/isa-bus.c b/hw/isa/isa-bus.c
index 0ffbc8dd28..348e0eab9d 100644
--- a/hw/isa/isa-bus.c
+++ b/hw/isa/isa-bus.c
@@ -288,7 +288,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/lpc_ich9.c b/hw/isa/lpc_ich9.c
index 10d1ee8b93..59930dd9d0 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);
 }
 
@@ -386,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);
+        }
     }
 }
 
@@ -507,6 +566,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 +731,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,12 +769,15 @@ static const VMStateDescription vmstate_ich9_lpc = {
     },
     .subsections = (const VMStateDescription*[]) {
         &vmstate_ich9_rst_cnt,
+        &vmstate_ich9_smi_feat,
         NULL
     }
 };
 
 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/hw/isa/pc87312.c b/hw/isa/pc87312.c
index b1c1a0acb1..c707d24db4 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 db9eb29ea4..d1514a61b3 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 4418b44ca9..d3be0cfb3a 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 cf48f420cc..75877de11c 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 fd14d7a07e..846e903eb2 100644
--- a/hw/misc/ivshmem.c
+++ b/hw/misc/ivshmem.c
@@ -869,7 +869,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 3ca25894f1..86f5e383b0 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/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c
index 7a588a7ad4..92f091a613 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++) {
diff --git a/hw/timer/mc146818rtc.c b/hw/timer/mc146818rtc.c
index 637f8722a7..4165450250 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
diff --git a/hw/usb/ccid-card-passthru.c b/hw/usb/ccid-card-passthru.c
index 88cb6d8978..daab0d56cf 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 6066d9b0f7..6d5137383b 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 4a0ebbfb32..860f5c35eb 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/block/block.h b/include/block/block.h
index 8b0dcdaa70..4e81f2069b 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
diff --git a/include/exec/memory.h b/include/exec/memory.h
index a10044f08f..987f9251c6 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/include/hw/arm/exynos4210.h b/include/hw/arm/exynos4210.h
index 76bb6d4203..d9e08014d6 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 f25870b718..cac1b2ba43 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 2fa22bdab6..b5e11d4d43 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 ca75eb5e32..c836db4b74 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 297e2ebcda..08ae122386 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 0ca7c19410..83649324b6 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 c928d7d907..daebb076c2 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 8b4fc54953..634086b657 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 723a2753c8..8da965addb 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 c60bcabae3..7475b714de 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/apic_internal.h b/include/hw/i386/apic_internal.h
index 1209eb483a..20ad28c95b 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 {
diff --git a/include/hw/i386/ich9.h b/include/hw/i386/ich9.h
index 5fd7e97d23..18dcca7ebc 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 */
@@ -240,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 */
diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
index 5a20c5e38e..079e8d9393 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 */
 
@@ -381,6 +381,16 @@ 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",\
+    },\
+    {\
+        .driver   = TYPE_X86_CPU,\
+        .property = "vmware-cpuid-freq",\
+        .value    = "off",\
+    },
 
 #define PC_COMPAT_2_7 \
     HW_COMPAT_2_7 \
diff --git a/include/hw/m68k/mcf.h b/include/hw/m68k/mcf.h
index bf43998d9b..9a0bcfa0f4 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 14f502240e..fc6f673ea0 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-core.h b/include/hw/qdev-core.h
index 2c973473f7..b44b476765 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/include/hw/qdev-properties.h b/include/hw/qdev-properties.h
index 306bbab088..7ac315331a 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 e59b9e7c45..767a2df7e2 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 afbb9bc07c..61a345c269 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 a8f3afb03b..09c2ce5170 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 8cc532ec0e..e64b944d7c 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 9a8bcbde36..e95f28cfec 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 0a149428cf..da0e7dd494 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 */
 
@@ -69,14 +70,16 @@ 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;
 
 /* 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;
@@ -85,49 +88,23 @@ typedef struct CharBackend {
     int fe_open;
 } CharBackend;
 
-struct CharDriverState {
+typedef struct CharDriver CharDriver;
+
+struct Chardev {
+    Object parent_obj;
+
     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;
     char *filename;
     int logfd;
     int be_open;
-    int is_mux;
     guint fd_in_tag;
-    bool replay;
     DECLARE_BITMAP(features, QEMU_CHAR_FEATURE_LAST);
-    QTAILQ_ENTRY(CharDriverState) next;
+    QTAILQ_ENTRY(Chardev) next;
 };
 
 /**
- * qemu_chr_alloc:
- * @backend: the common backend config
- * @errp: pointer to a NULL-initialized error object
- *
- * Allocate and initialize a new CharDriverState.
- *
- * Returns: a newly allocated CharDriverState, or NULL on error.
- */
-CharDriverState *qemu_chr_alloc(ChardevCommon *backend, Error **errp);
-
-/**
  * @qemu_chr_new_from_opts:
  *
  * Create a new character backend from a QemuOpts list.
@@ -136,8 +113,8 @@ CharDriverState *qemu_chr_alloc(ChardevCommon *backend, Error **errp);
  *
  * 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:
@@ -159,7 +136,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);
 
 
 /**
@@ -197,7 +174,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:
@@ -205,14 +182,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:
@@ -258,6 +235,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);
@@ -366,7 +345,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:
@@ -378,7 +357,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:
@@ -388,7 +367,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:
@@ -397,7 +376,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:
@@ -408,7 +387,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:
@@ -416,7 +395,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:
@@ -461,33 +440,83 @@ 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);
-bool chr_is_ringbuf(const CharDriverState *chr);
+int qemu_chr_add_client(Chardev *s, int fd);
+Chardev *qemu_chr_find(const char *name);
 
-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);
 
-typedef void CharDriverParse(QemuOpts *opts, ChardevBackend *backend,
-                             Error **errp);
-typedef CharDriverState *CharDriverCreate(const char *id,
-                                          ChardevBackend *backend,
-                                          ChardevReturn *ret, bool *be_opened,
-                                          Error **errp);
+#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);
+};
 
-void register_char_driver(const char *name, ChardevBackendKind kind,
-                          CharDriverParse *parse, CharDriverCreate *create);
+Chardev *qemu_chardev_new(const char *id, const char *typename,
+                          ChardevBackend *backend, Error **errp);
 
-extern int term_escape_char;
+void register_char_driver(const CharDriver *driver);
 
+extern int term_escape_char;
 
 /* console.c */
-typedef CharDriverState *(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/include/sysemu/replay.h b/include/sysemu/replay.h
index abb35ca8c9..7aad20b07f 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 */
@@ -125,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. */
@@ -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/include/sysemu/sysemu.h b/include/sysemu/sysemu.h
index ff8ffb5e47..4d50694930 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);
@@ -189,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/console.h b/include/ui/console.h
index b59e7b8c15..af6350e96f 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/include/ui/gtk.h b/include/ui/gtk.h
index b3b50059c7..47ffddb5b4 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 75e12396bb..52a9f8808b 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/memory.c b/memory.c
index 2bfc37f65c..6498727552 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]");
     }
@@ -2558,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/migration/savevm.c b/migration/savevm.c
index 455d5bac1e..204012ecef 100644
--- a/migration/savevm.c
+++ b/migration/savevm.c
@@ -2042,38 +2042,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);
 
@@ -2082,7 +2084,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);
 
@@ -2128,13 +2130,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)
diff --git a/monitor.c b/monitor.c
index 8b06b63729..25a480a95e 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)
@@ -3193,8 +3195,8 @@ 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);
-            if (chr && chr_is_ringbuf(chr)) {
+            Chardev *chr = qemu_chr_find(chr_info->label);
+            if (chr && CHARDEV_IS_RINGBUF(chr)) {
                 readline_add_completion(rs, chr_info->label);
             }
         }
@@ -3983,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 9bfc736f55..4962976c22 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 b7d645617c..aa0aa98fa5 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 bcd1c5f57d..f97ec23345 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 7aff77ee4a..b0f0ab6cc8 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/qdev-monitor.c b/qdev-monitor.c
index 81d01df928..549f45f066 100644
--- a/qdev-monitor.c
+++ b/qdev-monitor.c
@@ -137,6 +137,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/qemu-char.c b/qemu-char.c
index d8da1677ff..6b4a299702 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,40 +157,10 @@ 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);
-
-CharDriverState *qemu_chr_alloc(ChardevCommon *backend, Error **errp)
-{
-    CharDriverState *chr = g_malloc0(sizeof(CharDriverState));
-    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;
-    }
-
-    return chr;
-}
-
-void qemu_chr_be_event(CharDriverState *s, int event)
+void qemu_chr_be_event(Chardev *s, int event)
 {
     CharBackend *be = s->be;
 
@@ -211,7 +181,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);
 }
@@ -219,7 +189,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;
@@ -244,15 +214,17 @@ 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)
 {
+    ChardevClass *cc = CHARDEV_GET_CLASS(s);
     int res = 0;
     *offset = 0;
 
     qemu_mutex_lock(&s->chr_write_lock);
     while (*offset < len) {
     retry:
-        res = s->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;
@@ -272,16 +244,22 @@ static int qemu_chr_fe_write_buffer(CharDriverState *s, const uint8_t *buf, int
     return res;
 }
 
+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;
+    ChardevClass *cc;
     int ret;
 
     if (!s) {
         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);
@@ -289,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->chr_write(s, buf, len);
+    ret = cc->chr_write(s, buf, len);
 
     if (ret > 0) {
         qemu_chr_fe_write_log(s, buf, ret);
@@ -298,19 +277,19 @@ 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);
     }
     
     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;
 
-    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);
@@ -319,7 +298,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);
     }
 
@@ -331,7 +310,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;
@@ -342,21 +321,22 @@ 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;
 
-    if (!s || !s->chr_sync_read) {
+    if (!s || !CHARDEV_GET_CLASS(s)->chr_sync_read) {
         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);
     }
 
     while (offset < len) {
     retry:
-        res = s->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;
@@ -367,7 +347,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;
@@ -380,7 +360,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;
@@ -388,19 +368,19 @@ 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->chr_ioctl || s->replay) {
+    if (!s || !CHARDEV_GET_CLASS(s)->chr_ioctl || qemu_chr_replay(s)) {
         res = -ENOTSUP;
     } else {
-        res = s->chr_ioctl(s, cmd, arg);
+        res = CHARDEV_GET_CLASS(s)->chr_ioctl(s, cmd, arg);
     }
 
     return res;
 }
 
-int qemu_chr_be_can_write(CharDriverState *s)
+int qemu_chr_be_can_write(Chardev *s)
 {
     CharBackend *be = s->be;
 
@@ -411,7 +391,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;
 
@@ -420,9 +400,9 @@ 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 (s->replay) {
+    if (qemu_chr_replay(s)) {
         if (replay_mode == REPLAY_MODE_PLAY) {
             return;
         }
@@ -434,12 +414,12 @@ 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 && s->replay) {
-        fprintf(stderr,
-                "Replay: get msgfd is not supported for serial devices yet\n");
+    if (s && qemu_chr_replay(s)) {
+        error_report("Replay: get msgfd is not supported "
+                     "for serial devices yet");
         exit(1);
     }
     return res;
@@ -447,41 +427,45 @@ 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;
     }
 
-    return s->get_msgfds ? s->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)
 {
-    CharDriverState *s = be->chr;
+    Chardev *s = be->chr;
 
     if (!s) {
         return -1;
     }
 
-    return s->set_msgfds ? s->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(CharDriverState *s, int fd)
+int qemu_chr_add_client(Chardev *s, int fd)
 {
-    return s->chr_add_client ? s->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)
 {
-    CharDriverState *s = be->chr;
+    Chardev *s = be->chr;
 
     if (!s) {
         return;
     }
 
-    if (s->chr_accept_input)
-        s->chr_accept_input(s);
+    if (CHARDEV_GET_CLASS(s)->chr_accept_input) {
+        CHARDEV_GET_CLASS(s)->chr_accept_input(s);
+    }
     qemu_notify_event();
 }
 
@@ -497,38 +481,110 @@ 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 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 CharDriverState *qemu_chr_open_null(const char *id,
-                                           ChardevBackend *backend,
-                                           ChardevReturn *ret,
-                                           bool *be_opened,
-                                           Error **errp)
+static void char_init(Object *obj)
 {
-    CharDriverState *chr;
-    ChardevCommon *common = backend->u.null.data;
+    Chardev *chr = CHARDEV(obj);
 
-    chr = qemu_chr_alloc(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);
     }
-    chr->chr_write = null_chr_write;
+    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 = {
+    .kind = CHARDEV_BACKEND_KIND_NULL,
+};
+
+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 */
 #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 {
+struct MuxChardev {
+    Chardev parent;
     CharBackend *backends[MAX_MUX];
     CharBackend chr;
     int focus;
@@ -543,15 +599,17 @@ 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;
 };
 
+#define MUX_CHARDEV(obj) OBJECT_CHECK(MuxChardev, (obj), TYPE_CHARDEV_MUX)
+
 /* 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 = chr->opaque;
+    MuxChardev *d = MUX_CHARDEV(chr);
     int ret;
     if (!d->timestamps) {
         ret = qemu_chr_fe_write(&d->chr, buf, len);
@@ -603,7 +661,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";
@@ -630,7 +688,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];
 
@@ -639,7 +697,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;
@@ -683,9 +741,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 = chr->opaque;
+    MuxChardev *d = MUX_CHARDEV(chr);
     int m = d->focus;
     CharBackend *be = d->backends[m];
 
@@ -698,8 +756,7 @@ static void mux_chr_accept_input(CharDriverState *chr)
 
 static int mux_chr_can_read(void *opaque)
 {
-    CharDriverState *chr = opaque;
-    MuxDriver *d = chr->opaque;
+    MuxChardev *d = MUX_CHARDEV(opaque);
     int m = d->focus;
     CharBackend *be = d->backends[m];
 
@@ -716,8 +773,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 = chr->opaque;
+    Chardev *chr = CHARDEV(opaque);
+    MuxChardev *d = MUX_CHARDEV(opaque);
     int m = d->focus;
     CharBackend *be = d->backends[m];
     int i;
@@ -739,8 +796,7 @@ static bool muxes_realized;
 
 static void mux_chr_event(void *opaque, int event)
 {
-    CharDriverState *chr = opaque;
-    MuxDriver *d = chr->opaque;
+    MuxChardev *d = MUX_CHARDEV(opaque);
     int i;
 
     if (!muxes_realized) {
@@ -766,11 +822,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 (chr->is_mux) {
-            MuxDriver *d = chr->opaque;
+        if (CHARDEV_IS_MUX(chr)) {
+            MuxChardev *d = MUX_CHARDEV(chr);
             int i;
 
             /* send OPENED to all already-attached FEs */
@@ -790,17 +846,22 @@ 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 = s->opaque;
-    CharDriverState *chr = qemu_chr_fe_get_driver(&d->chr);
+    MuxChardev *d = MUX_CHARDEV(s);
+    Chardev *chr = qemu_chr_fe_get_driver(&d->chr);
+    ChardevClass *cc = CHARDEV_GET_CLASS(chr);
+
+    if (!cc->chr_add_watch) {
+        return NULL;
+    }
 
-    return chr->chr_add_watch(chr, cond);
+    return cc->chr_add_watch(chr, cond);
 }
 
-static void mux_chr_free(struct CharDriverState *chr)
+static void mux_chr_free(struct Chardev *chr)
 {
-    MuxDriver *d = chr->opaque;
+    MuxChardev *d = MUX_CHARDEV(chr);
     int i;
 
     for (i = 0; i < d->mux_cnt; i++) {
@@ -810,12 +871,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)
+static void mux_chr_set_handlers(Chardev *chr, GMainContext *context)
 {
-    MuxDriver *d = chr->opaque;
+    MuxChardev *d = MUX_CHARDEV(chr);
 
     /* Fix up the real driver with mux routines */
     qemu_chr_fe_set_handlers(&d->chr,
@@ -826,9 +886,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 = chr->opaque;
+    MuxChardev *d = MUX_CHARDEV(chr);
 
     assert(focus >= 0);
     assert(focus < d->mux_cnt);
@@ -842,63 +902,40 @@ 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,
-                                          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;
-    CharDriverState *chr, *drv;
-    MuxDriver *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;
-    }
-
-    chr = qemu_chr_alloc(common, errp);
-    if (!chr) {
-        return NULL;
+        return;
     }
-    d = g_new0(MuxDriver, 1);
 
-    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
      */
     *be_opened = muxes_realized;
-    chr->is_mux = 1;
-    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);
 }
 
-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 (s->is_mux) {
-        MuxDriver *d = s->opaque;
+    if (CHARDEV_IS_MUX(s)) {
+        MuxChardev *d = MUX_CHARDEV(s);
 
         if (d->mux_cnt >= MAX_MUX) {
             goto unavailable;
@@ -922,10 +959,10 @@ unavailable:
     return false;
 }
 
-static bool qemu_chr_is_busy(CharDriverState *s)
+static bool qemu_chr_is_busy(Chardev *s)
 {
-    if (s->is_mux) {
-        MuxDriver *d = s->opaque;
+    if (CHARDEV_IS_MUX(s)) {
+        MuxChardev *d = MUX_CHARDEV(s);
         return d->mux_cnt >= 0;
     } else {
         return s->be != NULL;
@@ -941,8 +978,8 @@ void qemu_chr_fe_deinit(CharBackend *b)
         if (b->chr->be == b) {
             b->chr->be = NULL;
         }
-        if (b->chr->is_mux) {
-            MuxDriver *d = b->chr->opaque;
+        if (CHARDEV_IS_MUX(b->chr)) {
+            MuxChardev *d = MUX_CHARDEV(b->chr);
             d->backends[b->tag] = NULL;
         }
         b->chr = NULL;
@@ -957,7 +994,8 @@ void qemu_chr_fe_set_handlers(CharBackend *b,
                               GMainContext *context,
                               bool set_open)
 {
-    CharDriverState *s;
+    Chardev *s;
+    ChardevClass *cc;
     int fe_open;
 
     s = b->chr;
@@ -965,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);
@@ -975,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->chr_update_read_handler) {
-        s->chr_update_read_handler(s, context);
+    if (cc->chr_update_read_handler) {
+        cc->chr_update_read_handler(s, context);
     }
 
     if (set_open) {
@@ -992,7 +1031,7 @@ void qemu_chr_fe_set_handlers(CharBackend *b,
         }
     }
 
-    if (s->is_mux) {
+    if (CHARDEV_IS_MUX(s)) {
         mux_chr_set_handlers(s, context);
     }
 }
@@ -1003,7 +1042,7 @@ void qemu_chr_fe_take_focus(CharBackend *b)
         return;
     }
 
-    if (b->chr->is_mux) {
+    if (CHARDEV_IS_MUX(b->chr)) {
         mux_set_focus(b->chr, b->tag);
     }
 }
@@ -1084,7 +1123,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,
@@ -1132,7 +1171,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);
@@ -1180,25 +1219,28 @@ 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 FDCharDriver {
-    CharDriverState *chr;
+typedef struct FDChardev {
+    Chardev parent;
+    Chardev *chr;
     QIOChannel *ioc_in, *ioc_out;
     int max_size;
-} FDCharDriver;
+} 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(CharDriverState *chr, const uint8_t *buf, int len)
+static int fd_chr_write(Chardev *chr, const uint8_t *buf, int len)
 {
-    FDCharDriver *s = chr->opaque;
-    
+    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)
 {
-    CharDriverState *chr = opaque;
-    FDCharDriver *s = chr->opaque;
+    Chardev *chr = CHARDEV(opaque);
+    FDChardev *s = FD_CHARDEV(opaque);
     int len;
     uint8_t buf[READ_BUF_LEN];
     ssize_t ret;
@@ -1227,23 +1269,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 = chr->opaque;
+    Chardev *chr = CHARDEV(opaque);
+    FDChardev *s = FD_CHARDEV(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 = chr->opaque;
+    FDChardev *s = FD_CHARDEV(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 = chr->opaque;
+    FDChardev *s = FD_CHARDEV(chr);
 
     remove_fd_in_watch(chr);
     if (s->ioc_in) {
@@ -1254,9 +1296,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 = chr->opaque;
+    FDChardev *s = FD_CHARDEV(chr);
 
     remove_fd_in_watch(chr);
     if (s->ioc_in) {
@@ -1266,23 +1308,16 @@ 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);
 }
 
 /* open a character device to a unix fd */
-static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out,
-                                         ChardevCommon *backend, Error **errp)
+static void qemu_chr_open_fd(Chardev *chr,
+                             int fd_in, int fd_out)
 {
-    CharDriverState *chr;
-    FDCharDriver *s;
+    FDChardev *s = FD_CHARDEV(chr);
     char *name;
 
-    chr = qemu_chr_alloc(backend, errp);
-    if (!chr) {
-        return NULL;
-    }
-    s = g_new0(FDCharDriver, 1);
     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);
@@ -1293,28 +1328,36 @@ static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out,
     g_free(name);
     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 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 CharDriverState *qemu_chr_open_pipe(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);
@@ -1330,10 +1373,10 @@ static CharDriverState *qemu_chr_open_pipe(const char *id,
         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(fd_in, fd_out, common, errp);
+    qemu_chr_open_fd(chr, fd_in, fd_out);
 }
 
 /* init terminal so that we can grab keys */
@@ -1343,7 +1386,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)
 {
@@ -1357,7 +1400,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;
 
@@ -1379,31 +1422,28 @@ 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 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;
-    CharDriverState *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;
@@ -1416,18 +1456,12 @@ 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);
-    if (!chr) {
-        return NULL;
-    }
-    chr->chr_free = qemu_chr_free_stdio;
-    chr->chr_set_echo = qemu_chr_set_echo_stdio;
+    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__) \
@@ -1438,22 +1472,25 @@ static CharDriverState *qemu_chr_open_stdio(const char *id,
 #define HAVE_CHARDEV_PTY 1
 
 typedef struct {
+    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);
+#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 CharDriverState *chr = opaque;
-    PtyCharDriver *s = chr->opaque;
+    struct Chardev *chr = CHARDEV(opaque);
+    PtyChardev *s = PTY_CHARDEV(opaque);
 
     qemu_mutex_lock(&chr->chr_write_lock);
     s->timer_tag = 0;
@@ -1467,9 +1504,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 = chr->opaque;
+    PtyChardev *s = PTY_CHARDEV(chr);
     char *name;
 
     if (s->timer_tag) {
@@ -1489,9 +1526,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 = chr->opaque;
+    PtyChardev *s = PTY_CHARDEV(chr);
     GPollFD pfd;
     int rc;
     QIOChannelFile *fioc = QIO_CHANNEL_FILE(s->ioc);
@@ -1511,7 +1548,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);
@@ -1520,9 +1557,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 char_pty_chr_write(Chardev *chr, const uint8_t *buf, int len)
 {
-    PtyCharDriver *s = chr->opaque;
+    PtyChardev *s = PTY_CHARDEV(chr);
 
     if (!s->connected) {
         /* guest sends data, check for (re-)connect */
@@ -1534,9 +1571,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 = chr->opaque;
+    PtyChardev *s = PTY_CHARDEV(chr);
     if (!s->connected) {
         return NULL;
     }
@@ -1545,8 +1582,8 @@ 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;
+    Chardev *chr = CHARDEV(opaque);
+    PtyChardev *s = PTY_CHARDEV(opaque);
 
     s->read_bytes = qemu_chr_be_can_write(chr);
     return s->read_bytes;
@@ -1554,8 +1591,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 = chr->opaque;
+    Chardev *chr = CHARDEV(opaque);
+    PtyChardev *s = PTY_CHARDEV(opaque);
     gsize len;
     uint8_t buf[READ_BUF_LEN];
     ssize_t ret;
@@ -1579,8 +1616,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 = chr->opaque;
+    Chardev *chr = CHARDEV(opaque);
+    PtyChardev *s = PTY_CHARDEV(opaque);
 
     s->open_tag = 0;
     qemu_chr_be_generic_open(chr);
@@ -1588,9 +1625,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 = chr->opaque;
+    PtyChardev *s = PTY_CHARDEV(chr);
 
     if (!connected) {
         if (s->open_tag) {
@@ -1622,9 +1659,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 = chr->opaque;
+    PtyChardev *s = PTY_CHARDEV(chr);
 
     qemu_mutex_lock(&chr->chr_write_lock);
     pty_chr_state(chr, 0);
@@ -1634,62 +1671,63 @@ 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);
 }
 
-static CharDriverState *qemu_chr_open_pty(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)
 {
-    CharDriverState *chr;
-    PtyCharDriver *s;
+    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(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;
-
-    fprintf(stderr, "char device redirected to %s (label %s)\n",
-            pty_name, 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;
+    error_report("char device redirected to %s (label %s)",
+                 pty_name, chr->label);
 
+    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 = {
+    .kind = CHARDEV_BACKEND_KIND_PTY,
+};
+
+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,
                             int parity, int data_bits, int stop_bits)
 {
@@ -1805,9 +1843,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 = chr->opaque;
+    FDChardev *s = FD_CHARDEV(chr);
     QIOChannelFile *fioc = QIO_CHANNEL_FILE(s->ioc_in);
 
     switch(cmd) {
@@ -1875,27 +1913,10 @@ 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);
 }
-
-static CharDriverState *qemu_chr_open_tty_fd(int fd,
-                                             ChardevCommon *backend,
-                                             bool *be_opened,
-                                             Error **errp)
-{
-    CharDriverState *chr;
-
-    tty_serial_init(fd, 115200, 'N', 8, 1);
-    chr = qemu_chr_open_fd(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__ */
 
 #if defined(__linux__)
@@ -1903,11 +1924,15 @@ static CharDriverState *qemu_chr_open_tty_fd(int fd,
 #define HAVE_CHARDEV_PARPORT 1
 
 typedef struct {
+    Chardev parent;
     int fd;
     int mode;
-} ParallelCharDriver;
+} ParallelChardev;
+
+#define PARALLEL_CHARDEV(obj) \
+    OBJECT_CHECK(ParallelChardev, (obj), TYPE_CHARDEV_PARALLEL)
 
-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;
@@ -1918,9 +1943,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 = chr->opaque;
+    ParallelChardev *drv = PARALLEL_CHARDEV(chr);
     int fd = drv->fd;
     uint8_t b;
 
@@ -1999,47 +2024,32 @@ 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 = chr->opaque;
+    ParallelChardev *drv = PARALLEL_CHARDEV(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);
 }
 
-static CharDriverState *qemu_chr_open_pp_fd(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)
 {
-    CharDriverState *chr;
-    ParallelCharDriver *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(backend, errp);
-    if (!chr) {
-        return NULL;
+        return;
     }
 
-    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;
-
-    return chr;
 }
 #endif /* __linux__ */
 
@@ -2047,35 +2057,48 @@ static CharDriverState *qemu_chr_open_pp_fd(int fd,
 
 #define HAVE_CHARDEV_PARPORT 1
 
-static int pp_ioctl(CharDriverState *chr, int cmd, void *arg)
+typedef struct {
+    Chardev parent;
+    int fd;
+} ParallelChardev;
+
+#define PARALLEL_CHARDEV(obj)                                   \
+    OBJECT_CHECK(ParallelChardev, (obj), TYPE_CHARDEV_PARALLEL)
+
+static int pp_ioctl(Chardev *chr, int cmd, void *arg)
 {
-    int fd = (int)(intptr_t)chr->opaque;
+    ParallelChardev *drv = PARALLEL_CHARDEV(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:
@@ -2084,22 +2107,14 @@ static int pp_ioctl(CharDriverState *chr, int cmd, void *arg)
     return 0;
 }
 
-static CharDriverState *qemu_chr_open_pp_fd(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)
 {
-    CharDriverState *chr;
-
-    chr = qemu_chr_alloc(backend, errp);
-    if (!chr) {
-        return NULL;
-    }
-    chr->opaque = (void *)(intptr_t)fd;
-    chr->chr_write = null_chr_write;
-    chr->chr_ioctl = pp_ioctl;
+    ParallelChardev *drv = PARALLEL_CHARDEV(chr);
+    drv->fd = fd;
     *be_opened = false;
-    return chr;
 }
 #endif
 
@@ -2108,23 +2123,32 @@ static CharDriverState *qemu_chr_open_pp_fd(int fd,
 #define HAVE_CHARDEV_SERIAL 1
 
 typedef struct {
+    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;
+
+#define TYPE_CHARDEV_WIN "chardev-win"
+#define WIN_CHARDEV(obj) OBJECT_CHECK(WinChardev, (obj), TYPE_CHARDEV_WIN)
 
 typedef struct {
+    Chardev parent;
     HANDLE  hStdIn;
     HANDLE  hInputReadyEvent;
     HANDLE  hInputDoneEvent;
     HANDLE  hInputThread;
     uint8_t win_stdio_buf;
-} WinStdioCharState;
+} 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
@@ -2134,9 +2158,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 = chr->opaque;
+    WinChardev *s = WIN_CHARDEV(chr);
 
     if (s->hsend) {
         CloseHandle(s->hsend);
@@ -2158,9 +2182,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 = chr->opaque;
+    WinChardev *s = WIN_CHARDEV(chr);
     COMMCONFIG comcfg;
     COMMTIMEOUTS cto = { 0, 0, 0, 0, 0};
     COMSTAT comstat;
@@ -2226,9 +2250,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 = chr->opaque;
+    WinChardev *s = WIN_CHARDEV(chr);
     DWORD len, ret, size, err;
 
     len = len1;
@@ -2260,17 +2284,18 @@ 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 = chr->opaque;
+    WinChardev *s = WIN_CHARDEV(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 = chr->opaque;
+    WinChardev *s = WIN_CHARDEV(chr);
+
     int ret, err;
     uint8_t buf[READ_BUF_LEN];
     DWORD size;
@@ -2290,9 +2315,9 @@ static void win_chr_readfile(CharDriverState *chr)
     }
 }
 
-static void win_chr_read(CharDriverState *chr)
+static void win_chr_read(Chardev *chr)
 {
-    WinCharState *s = chr->opaque;
+    WinChardev *s = WIN_CHARDEV(chr);
 
     if (s->len > s->max_size)
         s->len = s->max_size;
@@ -2304,8 +2329,8 @@ static void win_chr_read(CharDriverState *chr)
 
 static int win_chr_poll(void *opaque)
 {
-    CharDriverState *chr = opaque;
-    WinCharState *s = chr->opaque;
+    Chardev *chr = CHARDEV(opaque);
+    WinChardev *s = WIN_CHARDEV(opaque);
     COMSTAT status;
     DWORD comerr;
 
@@ -2319,34 +2344,10 @@ static int win_chr_poll(void *opaque)
     return 0;
 }
 
-static CharDriverState *qemu_chr_open_win_path(const char *filename,
-                                               ChardevCommon *backend,
-                                               Error **errp)
-{
-    CharDriverState *chr;
-    WinCharState *s;
-
-    chr = qemu_chr_alloc(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);
-        qemu_chr_free_common(chr);
-        return NULL;
-    }
-    return chr;
-}
-
 static int win_chr_pipe_poll(void *opaque)
 {
-    CharDriverState *chr = opaque;
-    WinCharState *s = chr->opaque;
+    Chardev *chr = CHARDEV(opaque);
+    WinChardev *s = WIN_CHARDEV(opaque);
     DWORD size;
 
     PeekNamedPipe(s->hcom, NULL, 0, NULL, &size, NULL);
@@ -2359,10 +2360,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 = chr->opaque;
+    WinChardev *s = WIN_CHARDEV(chr);
     OVERLAPPED ov;
     int ret;
     DWORD size;
@@ -2424,65 +2425,69 @@ static int win_chr_pipe_init(CharDriverState *chr, const char *filename,
 }
 
 
-static CharDriverState *qemu_chr_open_pipe(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;
-    CharDriverState *chr;
-    WinCharState *s;
-    ChardevCommon *common = qapi_ChardevHostdev_base(opts);
-
-    chr = qemu_chr_alloc(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);
-        qemu_chr_free_common(chr);
-        return NULL;
+        return;
     }
-    return chr;
 }
 
-static CharDriverState *qemu_chr_open_win_file(HANDLE fd_out,
-                                               ChardevCommon *backend,
-                                               Error **errp)
+static void qemu_chr_open_win_file(Chardev *chr, HANDLE fd_out)
 {
-    CharDriverState *chr;
-    WinCharState *s;
+    WinChardev *s = WIN_CHARDEV(chr);
 
-    chr = qemu_chr_alloc(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,
-                                              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(GetStdHandle(STD_OUTPUT_HANDLE),
-                                  common, errp);
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->chr_write = win_chr_write;
+    cc->chr_free = win_chr_free;
 }
 
-static int win_stdio_write(CharDriverState *chr, const uint8_t *buf, int len)
+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,
+};
+
+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)
 {
     HANDLE  hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
     DWORD   dwSize;
@@ -2503,8 +2508,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 = chr->opaque;
+    Chardev *chr = CHARDEV(opaque);
+    WinStdioChardev *stdio = WIN_STDIO_CHARDEV(opaque);
     INPUT_RECORD       buf[4];
     int                ret;
     DWORD              dwSize;
@@ -2537,8 +2542,7 @@ static void win_stdio_wait_func(void *opaque)
 
 static DWORD WINAPI win_stdio_thread(LPVOID param)
 {
-    CharDriverState   *chr   = param;
-    WinStdioCharState *stdio = chr->opaque;
+    WinStdioChardev *stdio = WIN_STDIO_CHARDEV(param);
     int                ret;
     DWORD              dwSize;
 
@@ -2576,8 +2580,8 @@ static DWORD WINAPI win_stdio_thread(LPVOID param)
 
 static void win_stdio_thread_wait_func(void *opaque)
 {
-    CharDriverState   *chr   = opaque;
-    WinStdioCharState *stdio = chr->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);
@@ -2586,9 +2590,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  = chr->opaque;
+    WinStdioChardev *stdio = WIN_STDIO_CHARDEV(chr);
     DWORD              dwMode = 0;
 
     GetConsoleMode(stdio->hStdIn, &dwMode);
@@ -2600,9 +2604,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 = chr->opaque;
+    WinStdioChardev *stdio = WIN_STDIO_CHARDEV(chr);
 
     if (stdio->hInputReadyEvent != INVALID_HANDLE_VALUE) {
         CloseHandle(stdio->hInputReadyEvent);
@@ -2613,40 +2617,32 @@ 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 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)
 {
-    CharDriverState   *chr;
-    WinStdioCharState *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(common, errp);
-    if (!chr) {
-        return NULL;
-    }
-    stdio = g_new0(WinStdioCharState, 1);
 
     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;
 
-    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,
                                  win_stdio_wait_func, chr)) {
@@ -2687,10 +2683,9 @@ 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;
+    return;
 
 err3:
     qemu_del_wait_object(stdio->hInputReadyEvent, NULL, NULL);
@@ -2699,7 +2694,6 @@ err2:
     CloseHandle(stdio->hInputDoneEvent);
 err1:
     qemu_del_wait_object(stdio->hStdIn, NULL, NULL);
-    return NULL;
 }
 #endif /* !_WIN32 */
 
@@ -2707,17 +2701,20 @@ err1:
 /* UDP Net console */
 
 typedef struct {
+    Chardev parent;
     QIOChannel *ioc;
     uint8_t buf[READ_BUF_LEN];
     int bufcnt;
     int bufptr;
     int max_size;
-} NetCharDriver;
+} UdpChardev;
+
+#define UDP_CHARDEV(obj) OBJECT_CHECK(UdpChardev, (obj), TYPE_CHARDEV_UDP)
 
 /* 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 = chr->opaque;
+    UdpChardev *s = UDP_CHARDEV(chr);
 
     return qio_channel_write(
         s->ioc, (const char *)buf, len, NULL);
@@ -2725,8 +2722,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 = chr->opaque;
+    Chardev *chr = CHARDEV(opaque);
+    UdpChardev *s = UDP_CHARDEV(opaque);
 
     s->max_size = qemu_chr_be_can_write(chr);
 
@@ -2743,8 +2740,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 = chr->opaque;
+    Chardev *chr = CHARDEV(opaque);
+    UdpChardev *s = UDP_CHARDEV(opaque);
     ssize_t ret;
 
     if (s->max_size == 0) {
@@ -2768,10 +2765,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 = chr->opaque;
+    UdpChardev *s = UDP_CHARDEV(chr);
 
     remove_fd_in_watch(chr);
     if (s->ioc) {
@@ -2782,48 +2779,22 @@ 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 = chr->opaque;
+    UdpChardev *s = UDP_CHARDEV(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);
 }
 
-static CharDriverState *qemu_chr_open_udp(QIOChannelSocket *sioc,
-                                          ChardevCommon *backend,
-                                          bool *be_opened,
-                                          Error **errp)
-{
-    CharDriverState *chr = NULL;
-    NetCharDriver *s = NULL;
-
-    chr = qemu_chr_alloc(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;
-    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;
-}
-
 /***********************************************************/
 /* TCP Net console */
 
 typedef struct {
+    Chardev parent;
     QIOChannel *ioc; /* Client I/O channel */
     QIOChannelSocket *sioc; /* Client master channel */
     QIOChannelSocket *listen_ioc;
@@ -2846,14 +2817,18 @@ typedef struct {
     guint reconnect_timer;
     int64_t reconnect_time;
     bool connect_err_reported;
-} TCPCharDriver;
+} 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(CharDriverState *chr)
+static void qemu_chr_socket_restart_timer(Chardev *chr)
 {
-    TCPCharDriver *s = chr->opaque;
+    SocketChardev *s = SOCKET_CHARDEV(chr);
     char *name;
+
     assert(s->connected == 0);
     s->reconnect_timer = g_timeout_add_seconds(s->reconnect_time,
                                                socket_reconnect_timeout, chr);
@@ -2862,10 +2837,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 = chr->opaque;
+    SocketChardev *s = SOCKET_CHARDEV(chr);
 
     if (!s->connect_err_reported) {
         error_report("Unable to connect character device %s: %s",
@@ -2880,9 +2855,10 @@ 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 = chr->opaque;
+    SocketChardev *s = SOCKET_CHARDEV(chr);
+
     if (s->connected) {
         int ret =  io_channel_send_full(s->ioc, buf, len,
                                         s->write_msgfds,
@@ -2904,8 +2880,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 = chr->opaque;
+    Chardev *chr = CHARDEV(opaque);
+    SocketChardev *s = SOCKET_CHARDEV(opaque);
     if (!s->connected)
         return 0;
     s->max_size = qemu_chr_be_can_write(chr);
@@ -2914,8 +2890,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,
+                                      SocketChardev *s,
                                       uint8_t *buf, int *size)
 {
     /* Handle any telnet client's basic IAC options to satisfy char by
@@ -2962,9 +2938,10 @@ 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 = chr->opaque;
+    SocketChardev *s = SOCKET_CHARDEV(chr);
+
     int to_copy = (s->read_msgfds_num < num) ? s->read_msgfds_num : num;
 
     assert(num <= TCP_MAX_FDS);
@@ -2987,9 +2964,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 = chr->opaque;
+    SocketChardev *s = SOCKET_CHARDEV(chr);
 
     /* clear old pending fd array */
     g_free(s->write_msgfds);
@@ -3012,9 +2989,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 = chr->opaque;
+    SocketChardev *s = SOCKET_CHARDEV(chr);
     struct iovec iov = { .iov_base = buf, .iov_len = len };
     int ret;
     size_t i;
@@ -3069,15 +3046,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 = chr->opaque;
+    SocketChardev *s = SOCKET_CHARDEV(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 = chr->opaque;
+    SocketChardev *s = SOCKET_CHARDEV(chr);
     int i;
 
     if (!s->connected) {
@@ -3104,9 +3081,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 = chr->opaque;
+    SocketChardev *s = SOCKET_CHARDEV(chr);
 
     if (!s->connected) {
         return;
@@ -3128,8 +3105,8 @@ 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;
+    Chardev *chr = CHARDEV(opaque);
+    SocketChardev *s = SOCKET_CHARDEV(opaque);
     uint8_t buf[READ_BUF_LEN];
     int len, size;
 
@@ -3153,9 +3130,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 = chr->opaque;
+    SocketChardev *s = SOCKET_CHARDEV(chr);
     int size;
 
     if (!s->connected) {
@@ -3173,8 +3150,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 = chr->opaque;
+    Chardev *chr = CHARDEV(opaque);
+    SocketChardev *s = SOCKET_CHARDEV(opaque);
 
     g_free(chr->filename);
     chr->filename = sockaddr_to_str(
@@ -3192,10 +3169,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 = chr->opaque;
+    SocketChardev *s = SOCKET_CHARDEV(chr);
 
     if (!s->connected) {
         return;
@@ -3211,7 +3188,7 @@ static void tcp_chr_update_read_handler(CharDriverState *chr,
 }
 
 typedef struct {
-    CharDriverState *chr;
+    Chardev *chr;
     char buf[12];
     size_t buflen;
 } TCPCharDriverTelnetInit;
@@ -3244,9 +3221,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 = chr->opaque;
+    SocketChardev *s = SOCKET_CHARDEV(chr);
     TCPCharDriverTelnetInit *init =
         g_new0(TCPCharDriverTelnetInit, 1);
     size_t n = 0;
@@ -3280,8 +3257,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 = chr->opaque;
+    Chardev *chr = user_data;
+    SocketChardev *s = user_data;
 
     if (qio_task_propagate_error(task, NULL)) {
         tcp_chr_disconnect(chr);
@@ -3295,9 +3272,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 = chr->opaque;
+    SocketChardev *s = SOCKET_CHARDEV(chr);
     QIOChannelTLS *tioc;
     Error *err = NULL;
     gchar *name;
@@ -3333,10 +3310,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 = chr->opaque;
+    SocketChardev *s = SOCKET_CHARDEV(chr);
     char *name;
     name = g_strdup_printf("chardev-tcp-%s-%s",
                            s->is_listen ? "server" : "client",
@@ -3346,9 +3323,10 @@ 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 = chr->opaque;
+    SocketChardev *s = SOCKET_CHARDEV(chr);
+
     if (s->ioc != NULL) {
 	return -1;
     }
@@ -3382,7 +3360,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;
@@ -3401,7 +3379,7 @@ static gboolean tcp_chr_accept(QIOChannel *channel,
                                GIOCondition cond,
                                void *opaque)
 {
-    CharDriverState *chr = opaque;
+    Chardev *chr = CHARDEV(opaque);
     QIOChannelSocket *sioc;
 
     sioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(channel),
@@ -3417,17 +3395,17 @@ 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 = chr->opaque;
+    SocketChardev *s = SOCKET_CHARDEV(chr);
     QIOChannelSocket *sioc;
 
     /* It can't wait on s->connected, since it is set asynchronously
      * 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);
@@ -3446,10 +3424,12 @@ 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->chr_wait_connected) {
-        return chr->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;
@@ -3465,9 +3445,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 = chr->opaque;
+    SocketChardev *s = SOCKET_CHARDEV(chr);
 
     tcp_chr_free_connection(chr);
 
@@ -3486,7 +3466,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);
 }
 
@@ -3494,8 +3474,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 = chr->opaque;
+    Chardev *chr = CHARDEV(opaque);
+    SocketChardev *s = SOCKET_CHARDEV(chr);
     Error *err = NULL;
 
     if (qio_task_propagate_error(task, &err)) {
@@ -3516,23 +3496,27 @@ static void qemu_chr_socket_connected(QIOTask *task, void *opaque)
 /* Ring buffer chardev */
 
 typedef struct {
+    Chardev parent;
     size_t size;
     size_t prod;
     size_t cons;
     uint8_t *cbuf;
-} RingBufCharDriver;
+} RingBufChardev;
+
+#define RINGBUF_CHARDEV(obj)                                    \
+    OBJECT_CHECK(RingBufChardev, (obj), TYPE_CHARDEV_RINGBUF)
 
-static size_t ringbuf_count(const CharDriverState *chr)
+static size_t ringbuf_count(const Chardev *chr)
 {
-    const RingBufCharDriver *d = chr->opaque;
+    const RingBufChardev *d = RINGBUF_CHARDEV(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 = chr->opaque;
+    RingBufChardev *d = RINGBUF_CHARDEV(chr);
     int i;
 
     if (!buf || (len < 0)) {
@@ -3549,9 +3533,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 = chr->opaque;
+    RingBufChardev *d = RINGBUF_CHARDEV(chr);
     int i;
 
     qemu_mutex_lock(&chr->chr_write_lock);
@@ -3563,66 +3547,39 @@ 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 = chr->opaque;
+    RingBufChardev *d = RINGBUF_CHARDEV(chr);
 
     g_free(d->cbuf);
-    g_free(d);
-    chr->opaque = NULL;
 }
 
-static CharDriverState *qemu_chr_open_ringbuf(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);
-    CharDriverState *chr;
-    RingBufCharDriver *d;
-
-    chr = qemu_chr_alloc(common, errp);
-    if (!chr) {
-        return NULL;
-    }
-    d = g_malloc(sizeof(*d));
+    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);
-
-    chr->opaque = d;
-    chr->chr_write = ringbuf_chr_write;
-    chr->chr_free = ringbuf_chr_free;
-
-    return chr;
-
-fail:
-    g_free(d);
-    qemu_chr_free_common(chr);
-    return NULL;
-}
-
-bool chr_is_ringbuf(const CharDriverState *chr)
-{
-    return chr->chr_write == ringbuf_chr_write;
 }
 
 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;
@@ -3633,7 +3590,7 @@ void qmp_ringbuf_write(const char *device, const char *data,
         return;
     }
 
-    if (!chr_is_ringbuf(chr)) {
+    if (!CHARDEV_IS_RINGBUF(chr)) {
         error_setg(errp,"%s is not a ringbuf device", device);
         return;
     }
@@ -3666,7 +3623,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;
@@ -3677,7 +3634,7 @@ char *qmp_ringbuf_read(const char *device, int64_t size,
         return NULL;
     }
 
-    if (!chr_is_ringbuf(chr)) {
+    if (!CHARDEV_IS_RINGBUF(chr)) {
         error_setg(errp,"%s is not a ringbuf device", device);
         return NULL;
     }
@@ -3894,6 +3851,36 @@ 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,
+};
+
+static void char_stdio_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->open = qemu_chr_open_stdio;
+#ifdef _WIN32
+    cc->chr_write = win_stdio_write;
+    cc->chr_set_echo = qemu_chr_set_echo_win_stdio;
+    cc->chr_free = win_stdio_free;
+#else
+    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
 static void qemu_chr_parse_serial(QemuOpts *opts, ChardevBackend *backend,
                                   Error **errp)
@@ -3943,6 +3930,28 @@ 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,
+};
+
+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
+    .parent = TYPE_CHARDEV_WIN,
+#else
+    .parent = TYPE_CHARDEV_FD,
+#endif
+    .class_init = char_pipe_class_init,
+};
+
 static void qemu_chr_parse_ringbuf(QemuOpts *opts, ChardevBackend *backend,
                                    Error **errp)
 {
@@ -3959,6 +3968,38 @@ 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,
+};
+
+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 = {
+    .kind = CHARDEV_BACKEND_KIND_MEMORY,
+    .parse = qemu_chr_parse_ringbuf,
+};
+
+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,
                                Error **errp)
 {
@@ -3974,6 +4015,29 @@ 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,
+};
+
+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,
                                   Error **errp)
 {
@@ -4101,54 +4165,47 @@ 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;
+static const CharDriver *backends[CHARDEV_BACKEND_KIND__MAX];
 
-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);
+    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;
-    CharDriver *cd;
-    CharDriverState *chr;
-    GSList *i;
+    const CharDriver *cd = NULL;
+    Chardev *chr;
+    int 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"))) {
-        fprintf(stderr, "Available chardev backend types:\n");
-        for (i = backends; i; i = i->next) {
-            cd = i->data;
-            fprintf(stderr, "%s\n", cd->name);
+    if (is_help_option(name)) {
+        GString *str = g_string_new("");
+        for (i = 0; i < ARRAY_SIZE(backends); i++) {
+            cd = backends[i];
+            if (cd) {
+                g_string_append_printf(str, "\n%s", ChardevBackendKind_lookup[cd->kind]);
+                if (cd->alias) {
+                    g_string_append_printf(str, "\n%s", cd->alias);
+                }
+            }
         }
-        exit(!is_help_option(qemu_opt_get(opts, "backend")));
+
+        error_report("Available chardev backend types: %s", str->str);
+        g_string_free(str, true);
+        exit(0);
     }
 
     if (id == NULL) {
@@ -4156,16 +4213,18 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
         goto err;
     }
 
-    for (i = backends; i; i = i->next) {
-        cd = i->data;
-
-        if (strcmp(cd->name, qemu_opt_get(opts, "backend")) == 0) {
+    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) {
-        error_setg(errp, "chardev: backend \"%s\" not found",
-                   qemu_opt_get(opts, "backend"));
+    if (i == ARRAY_SIZE(backends)) {
+        error_setg(errp, "chardev: backend \"%s\" not found", name);
         goto err;
     }
 
@@ -4222,10 +4281,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;
 
@@ -4248,15 +4307,17 @@ 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) {
-        chr->replay = replay_mode != REPLAY_MODE_NONE;
-        if (chr->replay && chr->chr_ioctl) {
-            fprintf(stderr,
-                    "Replay: ioctl is not supported for serial devices yet\n");
+        if (replay_mode != REPLAY_MODE_NONE) {
+            qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_REPLAY);
+        }
+        if (qemu_chr_replay(chr) && CHARDEV_GET_CLASS(chr)->chr_ioctl) {
+            error_report("Replay: ioctl is not supported "
+                         "for serial devices yet");
         }
         replay_register_char_driver(chr);
     }
@@ -4265,16 +4326,16 @@ 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->chr_set_echo) {
-        chr->chr_set_echo(chr, echo);
+    if (chr && CHARDEV_GET_CLASS(chr)->chr_set_echo) {
+        CHARDEV_GET_CLASS(chr)->chr_set_echo(chr, echo);
     }
 }
 
 void qemu_chr_fe_set_open(CharBackend *be, int fe_open)
 {
-    CharDriverState *chr = be->chr;
+    Chardev *chr = be->chr;
 
     if (!chr) {
         return;
@@ -4284,23 +4345,23 @@ 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 (CHARDEV_GET_CLASS(chr)->chr_set_fe_open) {
+        CHARDEV_GET_CLASS(chr)->chr_set_fe_open(chr, 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;
 
-    if (!s || s->chr_add_watch == NULL) {
+    if (!s || CHARDEV_GET_CLASS(s)->chr_add_watch == NULL) {
         return 0;
     }
 
-    src = s->chr_add_watch(s, cond);
+    src = CHARDEV_GET_CLASS(s)->chr_add_watch(s, cond);
     if (!src) {
         return 0;
     }
@@ -4314,36 +4375,22 @@ 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->chr_disconnect) {
-        chr->chr_disconnect(chr);
+    if (chr && CHARDEV_GET_CLASS(chr)->chr_disconnect) {
+        CHARDEV_GET_CLASS(chr)->chr_disconnect(chr);
     }
 }
 
-static void qemu_chr_free_common(CharDriverState *chr)
+void qemu_chr_free(Chardev *chr)
 {
-    if (chr->be) {
-        chr->be->chr = NULL;
+    if (CHARDEV_GET_CLASS(chr)->chr_free) {
+        CHARDEV_GET_CLASS(chr)->chr_free(chr);
     }
-    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(CharDriverState *chr)
-{
-    if (chr->chr_free) {
-        chr->chr_free(chr);
-    }
-    qemu_chr_free_common(chr);
+    object_unref(OBJECT(chr));
 }
 
-void qemu_chr_delete(CharDriverState *chr)
+void qemu_chr_delete(Chardev *chr)
 {
     QTAILQ_REMOVE(&chardevs, chr, next);
     qemu_chr_free(chr);
@@ -4352,7 +4399,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));
@@ -4368,28 +4415,41 @@ 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;
+    const CharDriver *c;
+    int 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);
+    for (i = 0; i < ARRAY_SIZE(backends); i++) {
+        c = backends[i];
+        if (!c) {
+            continue;
+        }
 
-        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;
 }
 
-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)
@@ -4495,21 +4555,19 @@ QemuOptsList qemu_chardev_opts = {
 
 #ifdef _WIN32
 
-static CharDriverState *qmp_chardev_open_file(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) {
@@ -4526,20 +4584,20 @@ static CharDriverState *qmp_chardev_open_file(const char *id,
                      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(out, common, errp);
+
+    qemu_chr_open_win_file(chr, out);
 }
 
-static CharDriverState *qmp_chardev_open_serial(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);
-    return qemu_chr_open_win_path(serial->device, common, errp);
+
+    win_chr_init(chr, serial->device, errp);
 }
 
 #else /* WIN32 */
@@ -4556,14 +4614,12 @@ static int qmp_chardev_open_file_source(char *src, int flags,
     return fd;
 }
 
-static CharDriverState *qmp_chardev_open_file(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;
@@ -4575,7 +4631,7 @@ static CharDriverState *qmp_chardev_open_file(const char *id,
 
     out = qmp_chardev_open_file_source(file->out, flags, errp);
     if (out < 0) {
-        return NULL;
+        return;
     }
 
     if (file->has_in) {
@@ -4583,58 +4639,141 @@ static CharDriverState *qmp_chardev_open_file(const char *id,
         in = qmp_chardev_open_file_source(file->in, flags, errp);
         if (in < 0) {
             qemu_close(out);
-            return NULL;
+            return;
         }
     }
 
-    return qemu_chr_open_fd(in, out, common, errp);
+    qemu_chr_open_fd(chr, in, out);
 }
 
 #ifdef HAVE_CHARDEV_SERIAL
-static CharDriverState *qmp_chardev_open_serial(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);
-    return qemu_chr_open_tty_fd(fd, common, be_opened, errp);
+    tty_serial_init(fd, 115200, 'N', 8, 1);
+
+    qemu_chr_open_fd(chr, fd, fd);
 }
 #endif
 
 #ifdef HAVE_CHARDEV_PARPORT
-static CharDriverState *qmp_chardev_open_parallel(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(fd, common, be_opened, errp);
+    qemu_chr_open_pp_fd(chr, fd, be_opened, errp);
 }
+
+static const CharDriver parallel_driver = {
+    .kind = CHARDEV_BACKEND_KIND_PARALLEL,
+    .alias = "parport",
+    .parse = qemu_chr_parse_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__)
+    cc->chr_write = null_chr_write;
+    cc->chr_ioctl = pp_ioctl;
+    cc->chr_free = pp_free;
+#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
+    /* 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
 
 #endif /* WIN32 */
 
+static const CharDriver file_driver = {
+    .kind = CHARDEV_BACKEND_KIND_FILE,
+    .parse = qemu_chr_parse_file_out,
+};
+
+static void char_file_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->open = qmp_chardev_open_file;
+#ifdef _WIN32
+    /* 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
+    .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,
+};
+
+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
+    .parent = TYPE_CHARDEV_WIN,
+#else
+    .parent = TYPE_CHARDEV_FD,
+#endif
+    .class_init = char_serial_class_init,
+};
+#endif
+
 static gboolean socket_reconnect_timeout(gpointer opaque)
 {
-    CharDriverState *chr = opaque;
-    TCPCharDriver *s = chr->opaque;
+    Chardev *chr = CHARDEV(opaque);
+    SocketChardev *s = SOCKET_CHARDEV(opaque);
     QIOChannelSocket *sioc;
 
     s->reconnect_timer = 0;
@@ -4652,14 +4791,12 @@ static gboolean socket_reconnect_timeout(gpointer opaque)
     return false;
 }
 
-static CharDriverState *qmp_chardev_open_socket(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)
 {
-    CharDriverState *chr;
-    TCPCharDriver *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;
@@ -4667,15 +4804,8 @@ static CharDriverState *qmp_chardev_open_socket(const char *id,
     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(common, errp);
-    if (!chr) {
-        return NULL;
-    }
-    s = g_new0(TCPCharDriver, 1);
-
     s->is_unix = addr->type == SOCKET_ADDRESS_KIND_UNIX;
     s->is_listen = is_listen;
     s->is_telnet = is_telnet;
@@ -4720,17 +4850,6 @@ static CharDriverState *qmp_chardev_open_socket(const char *id,
         qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_FD_PASS);
     }
 
-    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;
 
@@ -4778,113 +4897,201 @@ static CharDriverState *qmp_chardev_open_socket(const char *id,
         }
     }
 
-    return chr;
+    return;
 
- error:
+error:
     if (sioc) {
         object_unref(OBJECT(sioc));
     }
     if (s->tls_creds) {
         object_unref(OBJECT(s->tls_creds));
     }
-    g_free(s);
-    qemu_chr_free_common(chr);
-    return NULL;
 }
 
-static CharDriverState *qmp_chardev_open_udp(const char *id,
-                                             ChardevBackend *backend,
-                                             ChardevReturn *ret,
-                                             bool *be_opened,
-                                             Error **errp)
+static const CharDriver socket_driver = {
+    .kind = CHARDEV_BACKEND_KIND_SOCKET,
+    .parse = qemu_chr_parse_socket,
+};
+
+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;
-    CharDriverState *chr;
+    UdpChardev *s = UDP_CHARDEV(chr);
 
     if (qio_channel_socket_dgram_sync(sioc,
                                       udp->local, udp->remote,
                                       errp) < 0) {
         object_unref(OBJECT(sioc));
-        return NULL;
+        return;
     }
-    chr = qemu_chr_open_udp(sioc, common, be_opened, errp);
 
     name = g_strdup_printf("chardev-udp-%s", chr->label);
     qio_channel_set_name(QIO_CHANNEL(sioc), name);
     g_free(name);
 
-    return chr;
+    s->ioc = QIO_CHANNEL(sioc);
+    /* be isn't opened until we get a connection */
+    *be_opened = false;
 }
 
+static const CharDriver udp_driver = {
+    .kind = CHARDEV_BACKEND_KIND_UDP,
+    .parse = qemu_chr_parse_udp,
+};
+
+static void char_udp_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
 
-bool qemu_chr_has_feature(CharDriverState *chr,
+    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,
                           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);
 }
 
-ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
-                               Error **errp)
+static const ChardevClass *char_get_class(const char *driver, Error **errp)
 {
-    ChardevReturn *ret = g_new0(ChardevReturn, 1);
-    CharDriverState *chr = NULL;
-    Error *local_err = NULL;
-    GSList *i;
-    CharDriver *cd;
-    bool be_opened = true;
+    ObjectClass *oc;
+    const ChardevClass *cc;
+    char *typename = g_strdup_printf("chardev-%s", driver);
 
-    chr = qemu_chr_find(id);
-    if (chr) {
-        error_setg(errp, "Chardev '%s' already exists", id);
-        goto out_error;
-    }
+    oc = object_class_by_name(typename);
+    g_free(typename);
 
-    for (i = backends; i; i = i->next) {
-        cd = i->data;
+    if (!object_class_dynamic_cast(oc, TYPE_CHARDEV)) {
+        error_setg(errp, "'%s' is not a valid char driver name", driver);
+        return NULL;
+    }
 
-        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;
-        }
+    if (object_class_is_abstract(oc)) {
+        error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "driver",
+                   "abstract device type");
+        return NULL;
     }
 
-    if (chr == NULL) {
-        assert(!i);
-        error_setg(errp, "chardev backend not available");
-        goto out_error;
+    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)
+{
+    Chardev *chr = NULL;
+    Error *local_err = NULL;
+    bool be_opened = true;
+
+    assert(g_str_has_prefix(typename, "chardev-"));
+
+    chr = CHARDEV(object_new(typename));
     chr->label = g_strdup(id);
+
+    qemu_char_open(chr, backend, &be_opened, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        object_unref(OBJECT(chr));
+        return NULL;
+    }
+
     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)
 {
-    CharDriverState *chr;
+    Chardev *chr;
 
     chr = qemu_chr_find(id);
     if (chr == NULL) {
@@ -4895,7 +5102,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;
@@ -4905,7 +5112,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);
@@ -4914,45 +5121,46 @@ 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);
-#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);
+    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, &char_serial_type_info },
 #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);
+        { &parallel_driver, &char_parallel_type_info },
 #endif
 #ifdef HAVE_CHARDEV_PTY
-    register_char_driver("pty", CHARDEV_BACKEND_KIND_PTY, NULL,
-                         qemu_chr_open_pty);
+        { &pty_driver, &char_pty_type_info },
 #endif
 #ifdef _WIN32
-    register_char_driver("console", CHARDEV_BACKEND_KIND_CONSOLE, NULL,
-                         qemu_chr_open_win_con);
+        { &console_driver, &char_console_type_info },
 #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);
+        { &pipe_driver, &char_pipe_type_info },
+        { &mux_driver, &char_mux_type_info },
+        { &memory_driver, &char_memory_type_info }
+    };
+    int 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
      * as part of realize functions like serial_isa_realizefn when -nographic
      * is specified
diff --git a/qemu-options.hx b/qemu-options.hx
index 588e5beab3..ad2f8fc873 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -2430,8 +2430,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.
@@ -3017,7 +3015,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:
@@ -3400,12 +3398,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
@@ -3438,6 +3436,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/qmp.c b/qmp.c
index 886059ef94..2834f02b69 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/qom/cpu.c b/qom/cpu.c
index 7f575879f6..e815db7799 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;
     /*
diff --git a/qtest.c b/qtest.c
index bd9d417812..1446719e8d 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 edf46ab9df..aa65955942 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/replay/replay-snapshot.c b/replay/replay-snapshot.c
index 498059734d..65e2d375c2 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 7f27cf17b0..1835b9902e 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/spice-qemu-char.c b/spice-qemu-char.c
index 276c4aef68..dd97c17fca 100644
--- a/spice-qemu-char.c
+++ b/spice-qemu-char.c
@@ -2,42 +2,51 @@
 #include "trace.h"
 #include "ui/qemu-spice.h"
 #include "sysemu/char.h"
+#include "qemu/error-report.h"
 #include <spice.h>
 #include <spice/protocol.h>
 
 
-typedef struct SpiceCharDriver {
-    CharDriverState*      chr;
-    SpiceCharDeviceInstance     sin;
+typedef struct SpiceChardev {
+    Chardev               parent;
+
+    SpiceCharDeviceInstance sin;
     bool                  active;
     bool                  blocked;
     const uint8_t         *datapos;
     int                   datalen;
-    QLIST_ENTRY(SpiceCharDriver) next;
-} SpiceCharDriver;
+    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;
-    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);
+    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;
 
     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;
@@ -49,7 +58,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) {
@@ -69,7 +78,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);
+    SpiceChardev *scd = container_of(sin, SpiceChardev, sin);
+    Chardev *chr = CHARDEV(scd);
     int chr_event;
 
     switch (event) {
@@ -81,20 +91,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);
+    SpiceChardev *scd = container_of(sin, SpiceChardev, sin);
+    Chardev *chr = CHARDEV(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);
 }
 
@@ -115,7 +126,7 @@ static SpiceCharDeviceInterface vmc_interface = {
 };
 
 
-static void vmc_register_interface(SpiceCharDriver *scd)
+static void vmc_register_interface(SpiceChardev *scd)
 {
     if (scd->active) {
         return;
@@ -126,7 +137,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;
@@ -166,9 +177,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 = chr->opaque;
+    SpiceChardev *scd = SPICE_CHARDEV(chr);
     SpiceCharSource *src;
 
     assert(cond & G_IO_OUT);
@@ -180,9 +191,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 = chr->opaque;
+    SpiceChardev *s = SPICE_CHARDEV(chr);
     int read_bytes;
 
     assert(s->datalen == 0);
@@ -199,9 +210,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 = chr->opaque;
+    SpiceChardev *s = SPICE_CHARDEV(chr);
 
     vmc_unregister_interface(s);
     QLIST_REMOVE(s, next);
@@ -210,12 +221,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)
+static void spice_vmc_set_fe_open(struct Chardev *chr, int fe_open)
 {
-    SpiceCharDriver *s = chr->opaque;
+    SpiceChardev *s = SPICE_CHARDEV(chr);
     if (fe_open) {
         vmc_register_interface(s);
     } else {
@@ -223,10 +233,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 = chr->opaque;
+    SpiceChardev *s = SPICE_CHARDEV(chr);
 
     if (fe_open) {
         spice_server_port_event(&s->sin, SPICE_PORT_EVENT_OPENED);
@@ -236,69 +246,31 @@ static void spice_port_set_fe_open(struct CharDriverState *chr, int fe_open)
 #endif
 }
 
-static void print_allowed_subtypes(void)
+static void spice_chr_accept_input(struct Chardev *chr)
 {
-    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 CharDriverState *chr)
-{
-    SpiceCharDriver *s = chr->opaque;
+    SpiceChardev *s = SPICE_CHARDEV(chr);
 
     spice_server_char_device_wakeup(&s->sin);
 }
 
-static CharDriverState *chr_open(const char *subtype,
-                                 void (*set_fe_open)(struct CharDriverState *,
-                                                     int),
-                                 ChardevCommon *backend,
-                                 Error **errp)
+static void chr_open(Chardev *chr, const char *subtype)
 {
-    CharDriverState *chr;
-    SpiceCharDriver *s;
+    SpiceChardev *s = SPICE_CHARDEV(chr);
 
-    chr = qemu_chr_alloc(backend, errp);
-    if (!chr) {
-        return NULL;
-    }
-    s = g_malloc0(sizeof(SpiceCharDriver));
-    s->chr = chr;
     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,
-                                                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) {
@@ -306,47 +278,46 @@ static CharDriverState *qemu_chr_open_spice_vmc(const char *id,
         }
     }
     if (*psubtype == NULL) {
-        fprintf(stderr, "spice-qemu-char: unsupported type: %s\n", type);
-        print_allowed_subtypes();
-        return NULL;
+        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;
     }
 
     *be_opened = false;
-    return chr_open(type, spice_vmc_set_fe_open, common, errp);
+    chr_open(chr, type);
 }
 
 #if SPICE_SERVER_VERSION >= 0x000c02
-static CharDriverState *qemu_chr_open_spice_port(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);
-    CharDriverState *chr;
-    SpiceCharDriver *s;
+    SpiceChardev *s;
 
     if (name == NULL) {
-        fprintf(stderr, "spice-qemu-char: missing name parameter\n");
-        return NULL;
+        error_setg(errp, "missing name parameter");
+        return;
     }
 
-    chr = chr_open("port", spice_port_set_fe_open, common, errp);
-    if (!chr) {
-        return NULL;
-    }
+    chr_open(chr, "port");
+
     *be_opened = false;
-    s = chr->opaque;
+    s = SPICE_CHARDEV(chr);
     s->sin.portname = g_strdup(name);
-
-    return chr;
 }
 
 void qemu_spice_register_ports(void)
 {
-    SpiceCharDriver *s;
+    SpiceChardev *s;
 
     QLIST_FOREACH(s, &spice_chars, next) {
         if (s->sin.portname == NULL) {
@@ -387,12 +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)
 {
-    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,
+    };
+    static const CharDriver port_driver = {
+        .kind = CHARDEV_BACKEND_KIND_SPICEPORT,
+        .parse = qemu_chr_parse_spice_port,
+    };
+    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/stubs/monitor.c b/stubs/monitor.c
index 1d574b1c6f..e018c8f594 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 d9a6da99d2..9c8aa48c9c 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/target/i386/cpu.c b/target/i386/cpu.c
index cff23e129d..eb49980ef1 100644
--- a/target/i386/cpu.c
+++ b/target/i386/cpu.c
@@ -3658,6 +3658,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, true),
     DEFINE_PROP_END_OF_LIST()
 };
 
diff --git a/target/i386/cpu.h b/target/i386/cpu.h
index 10c5a3538d..4d788d56fc 100644
--- a/target/i386/cpu.h
+++ b/target/i386/cpu.h
@@ -1214,6 +1214,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 8e130ccf9c..27fd0505df 100644
--- a/target/i386/kvm.c
+++ b/target/i386/kvm.c
@@ -982,12 +982,6 @@ int kvm_arch_init_vcpu(CPUState *cs)
         }
     }
 
-    cpuid_data.cpuid.padding = 0;
-    r = kvm_vcpu_ioctl(cs, KVM_SET_CPUID2, &cpuid_data);
-    if (r) {
-        goto fail;
-    }
-
     r = kvm_arch_set_tsc_khz(cs);
     if (r < 0) {
         goto fail;
@@ -1007,6 +1001,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) {
+        goto fail;
+    }
+
     if (has_xsave) {
         env->kvm_xsave_buf = qemu_memalign(4096, sizeof(struct kvm_xsave));
     }
diff --git a/target/i386/seg_helper.c b/target/i386/seg_helper.c
index fb79f3180d..d24574da7f 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)) {
diff --git a/tests/Makefile.include b/tests/Makefile.include
index 22ea256e94..33b4f88746 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)
diff --git a/tests/test-char.c b/tests/test-char.c
index 241685afbb..da69f110e4 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 96bf00eefa..f3ac6ea21a 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/translate-all.c b/translate-all.c
index 20262938bb..6d2fcabca7 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);
     }
 
diff --git a/ui/console.c b/ui/console.c
index b9575f2ee5..fe03a666f7 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,11 +1046,24 @@ void console_select(unsigned int index)
     }
 }
 
-static int console_puts(CharDriverState *chr, const uint8_t *buf, int len)
+typedef struct VCChardev {
+    Chardev parent;
+    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)
 {
-    QemuConsole *s = chr->opaque;
+    VCChardev *drv = VC_CHARDEV(chr);
+    QemuConsole *s = drv->console;
     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;
@@ -1129,13 +1142,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) {
@@ -1952,9 +1965,10 @@ 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 vc_chr_set_echo(Chardev *chr, bool echo)
 {
-    QemuConsole *s = chr->opaque;
+    VCChardev *drv = VC_CHARDEV(chr);
+    QemuConsole *s = drv->console;
 
     s->echo = echo;
 }
@@ -1992,16 +2006,13 @@ 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)
 {
-    QemuConsole *s;
+    VCChardev *drv = VC_CHARDEV(chr);
+    QemuConsole *s = drv->console;
     int g_width = 80 * FONT_WIDTH;
     int g_height = 24 * FONT_HEIGHT;
 
-    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);
@@ -2041,26 +2052,26 @@ static void text_console_do_init(CharDriverState *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;
     }
 
     qemu_chr_be_generic_open(chr);
 }
 
-static CharDriverState *text_console_init(ChardevVC *vc, Error **errp)
+static const CharDriver vc_driver;
+
+static void vc_chr_open(Chardev *chr,
+                        ChardevBackend *backend,
+                        bool *be_opened,
+                        Error **errp)
 {
-    ChardevCommon *common = qapi_ChardevVC_base(vc);
-    CharDriverState *chr;
+    ChardevVC *vc = backend->u.vc.data;
+    VCChardev *drv = VC_CHARDEV(chr);
     QemuConsole *s;
     unsigned width = 0;
     unsigned height = 0;
 
-    chr = qemu_chr_alloc(common, errp);
-    if (!chr) {
-        return NULL;
-    }
-
     if (vc->has_width) {
         width = vc->width;
     } else if (vc->has_cols) {
@@ -2082,37 +2093,21 @@ static CharDriverState *text_console_init(ChardevVC *vc, Error **errp)
     }
 
     if (!s) {
-        g_free(chr);
         error_setg(errp, "cannot create text console");
-        return NULL;
+        return;
     }
 
     s->chr = chr;
-    chr->opaque = s;
-    chr->chr_set_echo = text_console_set_echo;
+    drv->console = s;
 
     if (display_state) {
         text_console_do_init(chr, display_state);
     }
-    return chr;
-}
-
-static VcHandler *vc_handler = text_console_init;
 
-static CharDriverState *vc_init(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;
 }
 
 void qemu_console_resize(QemuConsole *s, int width, int height)
@@ -2150,8 +2145,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;
@@ -2191,12 +2185,39 @@ static const TypeInfo qemu_console_info = {
     .class_size = sizeof(QemuConsoleClass),
 };
 
+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,
+};
 
 static void register_types(void)
 {
     type_register_static(&qemu_console_info);
-    register_char_driver("vc", CHARDEV_BACKEND_KIND_VC, qemu_chr_parse_vc,
-                         vc_init);
 }
 
 type_init(register_types);
diff --git a/ui/gtk.c b/ui/gtk.c
index 86368e38b7..bdd831c268 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -181,6 +181,15 @@ struct GtkDisplayState {
     bool ignore_keys;
 };
 
+typedef struct VCChardev {
+    Chardev parent;
+    VirtualConsole *console;
+    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);
@@ -1683,50 +1692,70 @@ 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)
 {
-    VirtualConsole *vc = chr->opaque;
+    VCChardev *vcd = VC_CHARDEV(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)
 {
-    VirtualConsole *vc = chr->opaque;
+    VCChardev *vcd = VC_CHARDEV(chr);
+    VirtualConsole *vc = vcd->console;
 
-    vc->vte.echo = echo;
+    if (vc) {
+        vc->vte.echo = echo;
+    } else {
+        vcd->echo = echo;
+    }
 }
 
 static int nb_vcs;
-static CharDriverState *vcs[MAX_VCS];
+static Chardev *vcs[MAX_VCS];
+static const CharDriver gd_vc_driver;
 
-static CharDriverState *gd_vc_handler(ChardevVC *vc, Error **errp)
+static void gd_vc_open(Chardev *chr,
+                       ChardevBackend *backend,
+                       bool *be_opened,
+                       Error **errp)
 {
-    ChardevCommon *common = qapi_ChardevVC_base(vc);
-    CharDriverState *chr;
-
     if (nb_vcs == MAX_VCS) {
         error_setg(errp, "Maximum number of consoles reached");
-        return NULL;
-    }
-
-    chr = qemu_chr_alloc(common, errp);
-    if (!chr) {
-        return NULL;
+        return;
     }
 
-    chr->chr_write = gd_vc_chr_write;
-    chr->chr_set_echo = gd_vc_chr_set_echo;
+    vcs[nb_vcs++] = chr;
 
-    /* Temporary, until gd_vc_vte_init runs.  */
-    chr->opaque = g_new0(VirtualConsole, 1);
+    /* console/chardev init sometimes completes elsewhere in a 2nd
+     * stage, so defer OPENED events until they are fully initialized
+     */
+    *be_opened = false;
+}
 
-    vcs[nb_vcs++] = chr;
+static void char_gd_vc_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
 
-    return chr;
+    cc->open = gd_vc_open;
+    cc->chr_write = gd_vc_chr_write;
+    cc->chr_set_echo = gd_vc_chr_set_echo;
 }
 
+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,
+};
+
 static gboolean gd_vc_in(VteTerminal *terminal, gchar *text, guint size,
                          gpointer user_data)
 {
@@ -1755,21 +1784,19 @@ 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;
-    VirtualConsole *tmp_vc = chr->opaque;
+    VCChardev *vcd = VC_CHARDEV(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
@@ -2325,6 +2352,7 @@ void early_gtk_display_init(int opengl)
     }
 
 #if defined(CONFIG_VTE)
-    register_vc_handler(gd_vc_handler);
+    type_register(&char_gd_vc_type_info);
+    register_char_driver(&gd_vc_driver);
 #endif
 }
diff --git a/vl.c b/vl.c
index 68e8c003d1..0b72b12878 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;
@@ -465,6 +465,9 @@ static QemuOptsList qemu_icount_opts = {
         }, {
             .name = "rrfile",
             .type = QEMU_OPT_STRING,
+        }, {
+            .name = "rrsnapshot",
+            .type = QEMU_OPT_STRING,
         },
         { /* end of list */ }
     },
@@ -512,43 +515,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
  *
@@ -2356,7 +2322,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;
@@ -3042,9 +3008,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();
@@ -4289,6 +4252,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");
@@ -4634,7 +4599,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;
         }
diff --git a/xen-common-stub.c b/xen-common-stub.c
index 699c3f1c64..09fce2dd36 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 909976071c..fd2c92847e 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");