diff mbox

[19/38] char: make some qemu_chr_fe skip if no driver

Message ID 20161022095318.17775-20-marcandre.lureau@redhat.com
State New
Headers show

Commit Message

Marc-André Lureau Oct. 22, 2016, 9:52 a.m. UTC
In most cases, front ends do not care about the side effect of
CharBackend, so we can simply skip the checks and call the qemu_chr_fe
functions even without associated CharDriver.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 hw/arm/pxa2xx.c           |  8 +++-----
 hw/arm/strongarm.c        | 16 ++++++---------
 hw/char/bcm2835_aux.c     | 18 ++++++-----------
 hw/char/cadence_uart.c    | 24 +++++++---------------
 hw/char/digic-uart.c      | 14 +++++--------
 hw/char/escc.c            |  4 +---
 hw/char/etraxfs_ser.c     |  8 +++-----
 hw/char/imx_serial.c      | 26 +++++++++---------------
 hw/char/ipoctal232.c      | 14 +++++--------
 hw/char/lm32_juart.c      | 14 +++++--------
 hw/char/lm32_uart.c       | 14 +++++--------
 hw/char/mcf_uart.c        |  8 +++-----
 hw/char/milkymist-uart.c  | 10 +++-------
 hw/char/pl011.c           | 18 ++++++-----------
 hw/char/sclpconsole-lm.c  |  6 ++----
 hw/char/sclpconsole.c     |  6 ++----
 hw/char/stm32f2xx_usart.c | 22 +++++++-------------
 hw/char/virtio-console.c  |  6 ++----
 hw/char/xen_console.c     |  6 ++----
 hw/char/xilinx_uartlite.c | 14 +++++--------
 qemu-char.c               | 51 ++++++++++++++++++++++++++++++++++++++---------
 include/sysemu/char.h     | 40 +++++++++++++++++++++++++------------
 22 files changed, 156 insertions(+), 191 deletions(-)

Comments

Philippe Mathieu-Daudé Feb. 15, 2023, 10:13 p.m. UTC | #1
Hi Marc-André,

[very old patch...]

On 22/10/16 11:52, Marc-André Lureau wrote:
> In most cases, front ends do not care about the side effect of
> CharBackend, so we can simply skip the checks and call the qemu_chr_fe
> functions even without associated CharDriver.
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>   hw/arm/pxa2xx.c           |  8 +++-----
>   hw/arm/strongarm.c        | 16 ++++++---------
>   hw/char/bcm2835_aux.c     | 18 ++++++-----------
>   hw/char/cadence_uart.c    | 24 +++++++---------------

>   qemu-char.c               | 51 ++++++++++++++++++++++++++++++++++++++---------
>   include/sysemu/char.h     | 40 +++++++++++++++++++++++++------------
>   22 files changed, 156 insertions(+), 191 deletions(-)


> diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c
> index 4459b2d..291818e 100644
> --- a/hw/char/cadence_uart.c
> +++ b/hw/char/cadence_uart.c
> @@ -142,9 +142,7 @@ static void uart_rx_reset(CadenceUARTState *s)
>   {
>       s->rx_wpos = 0;
>       s->rx_count = 0;
> -    if (s->chr.chr) {
> -        qemu_chr_fe_accept_input(&s->chr);
> -    }
> +    qemu_chr_fe_accept_input(&s->chr);

I'm trying to understand this change. This code comes from:

commit 9121d02cb33c96b444a3973579f5edc119597e81

     char/cadence_uart: Fix reset for unattached instances

     commit 1db8b5efe0c2b5000e50691eea61264a615f43de introduced an issue
     where QEMU would segfault if you have an unattached Cadence UART.

     Fix by guarding the flush-on-reset logic on there being a qemu_chr
     attachment.

diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c
index 131370a74b..4d457f8c65 100644
--- a/hw/char/cadence_uart.c
+++ b/hw/char/cadence_uart.c
@@ -157,7 +157,9 @@ static void uart_rx_reset(UartState *s)
  {
      s->rx_wpos = 0;
      s->rx_count = 0;
-    qemu_chr_accept_input(s->chr);
+    if (s->chr) {
+        qemu_chr_accept_input(s->chr);
+    }

When resetting the xlnx-zcu102 machine, I hit:

(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = 
EXC_BAD_ACCESS (code=1, address=0x50)
   * frame #0: 0x10020a740 gd_vc_send_chars(vc=0x000000000) at 
gtk.c:1759:41 [opt]
     frame #1: 0x100636264 qemu_chr_fe_accept_input(be=<unavailable>) at 
char-fe.c:159:9 [opt]
     frame #2: 0x1000608e0 cadence_uart_reset_hold [inlined] 
uart_rx_reset(s=0x10810a960) at cadence_uart.c:158:5 [opt]
     frame #3: 0x1000608d4 cadence_uart_reset_hold(obj=0x10810a960) at 
cadence_uart.c:530:5 [opt]
     frame #4: 0x100580ab4 resettable_phase_hold(obj=0x10810a960, 
opaque=0x000000000, type=<unavailable>) at resettable.c:0 [opt]
     frame #5: 0x10057d1b0 bus_reset_child_foreach(obj=<unavailable>, 
cb=(resettable_phase_hold at resettable.c:162), opaque=0x000000000, 
type=RESET_TYPE_COLD) at bus.c:97:13 [opt]
     frame #6: 0x1005809f8 resettable_phase_hold [inlined] 
resettable_child_foreach(rc=0x000060000332d2c0, obj=0x0000600002c1c180, 
cb=<unavailable>, opaque=0x000000000, type=RESET_TYPE_COLD) at 
resettable.c:96:9 [opt]
     frame #7: 0x1005809d8 resettable_phase_hold(obj=0x0000600002c1c180, 
opaque=0x000000000, type=RESET_TYPE_COLD) at resettable.c:173:5 [opt]
     frame #8: 0x1005803a0 
resettable_assert_reset(obj=0x0000600002c1c180, type=<unavailable>) at 
resettable.c:60:5 [opt]
     frame #9: 0x10058027c resettable_reset(obj=0x0000600002c1c180, 
type=RESET_TYPE_COLD) at resettable.c:45:5 [opt]

Doing similar to commit 9121d02cb3...:

-- >8 --
diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c
index c069a30842..deadee1788 100644
--- a/hw/char/cadence_uart.c
+++ b/hw/char/cadence_uart.c
@@ -155,7 +155,9 @@ static void uart_rx_reset(CadenceUARTState *s)
  {
      s->rx_wpos = 0;
      s->rx_count = 0;
-    qemu_chr_fe_accept_input(&s->chr);
+    if (qemu_chr_fe_backend_open(&s->chr)) {
+        qemu_chr_fe_accept_input(&s->chr);
+    }
  }
---

... fixes the issue but I'm not sure 1/ this is a correct use of the
chardev API and 2/ this is how the HW work at reset.

Can you help me with 1/ before I ask Xilinx folks for 2/ ? :)

Thanks,

Phil.
Marc-André Lureau Feb. 16, 2023, 2:23 p.m. UTC | #2
Hi Philippe

On Thu, Feb 16, 2023 at 2:14 AM Philippe Mathieu-Daudé
<philmd@linaro.org> wrote:
>
> Hi Marc-André,
>
> [very old patch...]
>
> On 22/10/16 11:52, Marc-André Lureau wrote:
> > In most cases, front ends do not care about the side effect of
> > CharBackend, so we can simply skip the checks and call the qemu_chr_fe
> > functions even without associated CharDriver.
> >
> > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> > ---
> >   hw/arm/pxa2xx.c           |  8 +++-----
> >   hw/arm/strongarm.c        | 16 ++++++---------
> >   hw/char/bcm2835_aux.c     | 18 ++++++-----------
> >   hw/char/cadence_uart.c    | 24 +++++++---------------
>
> >   qemu-char.c               | 51 ++++++++++++++++++++++++++++++++++++++---------
> >   include/sysemu/char.h     | 40 +++++++++++++++++++++++++------------
> >   22 files changed, 156 insertions(+), 191 deletions(-)
>
>
> > diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c
> > index 4459b2d..291818e 100644
> > --- a/hw/char/cadence_uart.c
> > +++ b/hw/char/cadence_uart.c
> > @@ -142,9 +142,7 @@ static void uart_rx_reset(CadenceUARTState *s)
> >   {
> >       s->rx_wpos = 0;
> >       s->rx_count = 0;
> > -    if (s->chr.chr) {
> > -        qemu_chr_fe_accept_input(&s->chr);
> > -    }
> > +    qemu_chr_fe_accept_input(&s->chr);
>
> I'm trying to understand this change. This code comes from:
>
> commit 9121d02cb33c96b444a3973579f5edc119597e81
>
>      char/cadence_uart: Fix reset for unattached instances
>
>      commit 1db8b5efe0c2b5000e50691eea61264a615f43de introduced an issue
>      where QEMU would segfault if you have an unattached Cadence UART.
>
>      Fix by guarding the flush-on-reset logic on there being a qemu_chr
>      attachment.
>
> diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c
> index 131370a74b..4d457f8c65 100644
> --- a/hw/char/cadence_uart.c
> +++ b/hw/char/cadence_uart.c
> @@ -157,7 +157,9 @@ static void uart_rx_reset(UartState *s)
>   {
>       s->rx_wpos = 0;
>       s->rx_count = 0;
> -    qemu_chr_accept_input(s->chr);
> +    if (s->chr) {
> +        qemu_chr_accept_input(s->chr);
> +    }
>
> When resetting the xlnx-zcu102 machine, I hit:
>
> (lldb) bt
> * thread #1, queue = 'com.apple.main-thread', stop reason =
> EXC_BAD_ACCESS (code=1, address=0x50)
>    * frame #0: 0x10020a740 gd_vc_send_chars(vc=0x000000000) at
> gtk.c:1759:41 [opt]
>      frame #1: 0x100636264 qemu_chr_fe_accept_input(be=<unavailable>) at
> char-fe.c:159:9 [opt]
>      frame #2: 0x1000608e0 cadence_uart_reset_hold [inlined]
> uart_rx_reset(s=0x10810a960) at cadence_uart.c:158:5 [opt]
>      frame #3: 0x1000608d4 cadence_uart_reset_hold(obj=0x10810a960) at
> cadence_uart.c:530:5 [opt]
>      frame #4: 0x100580ab4 resettable_phase_hold(obj=0x10810a960,
> opaque=0x000000000, type=<unavailable>) at resettable.c:0 [opt]
>      frame #5: 0x10057d1b0 bus_reset_child_foreach(obj=<unavailable>,
> cb=(resettable_phase_hold at resettable.c:162), opaque=0x000000000,
> type=RESET_TYPE_COLD) at bus.c:97:13 [opt]
>      frame #6: 0x1005809f8 resettable_phase_hold [inlined]
> resettable_child_foreach(rc=0x000060000332d2c0, obj=0x0000600002c1c180,
> cb=<unavailable>, opaque=0x000000000, type=RESET_TYPE_COLD) at
> resettable.c:96:9 [opt]
>      frame #7: 0x1005809d8 resettable_phase_hold(obj=0x0000600002c1c180,
> opaque=0x000000000, type=RESET_TYPE_COLD) at resettable.c:173:5 [opt]
>      frame #8: 0x1005803a0
> resettable_assert_reset(obj=0x0000600002c1c180, type=<unavailable>) at
> resettable.c:60:5 [opt]
>      frame #9: 0x10058027c resettable_reset(obj=0x0000600002c1c180,
> type=RESET_TYPE_COLD) at resettable.c:45:5 [opt]
>
> Doing similar to commit 9121d02cb3...:
>
> -- >8 --
> diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c
> index c069a30842..deadee1788 100644
> --- a/hw/char/cadence_uart.c
> +++ b/hw/char/cadence_uart.c
> @@ -155,7 +155,9 @@ static void uart_rx_reset(CadenceUARTState *s)
>   {
>       s->rx_wpos = 0;
>       s->rx_count = 0;
> -    qemu_chr_fe_accept_input(&s->chr);
> +    if (qemu_chr_fe_backend_open(&s->chr)) {
> +        qemu_chr_fe_accept_input(&s->chr);
> +    }
>   }
> ---
>
> ... fixes the issue but I'm not sure 1/ this is a correct use of the
> chardev API and 2/ this is how the HW work at reset.

The trouble is that GTK/VTE console/chardev creation is done later.

I think we should rather fix ui/gtk.c, as this could happen with other
char frontends:

diff --git a/ui/gtk.c b/ui/gtk.c
index 4817623c8f..dfaf6d33c3 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -1783,7 +1783,9 @@ static void gd_vc_chr_accept_input(Chardev *chr)
     VCChardev *vcd = VC_CHARDEV(chr);
     VirtualConsole *vc = vcd->console;

-    gd_vc_send_chars(vc);
+    if (vc) {
+        gd_vc_send_chars(vc);
+    }
 }


>
> Can you help me with 1/ before I ask Xilinx folks for 2/ ? :)
>
> Thanks,
>
> Phil.
>
Philippe Mathieu-Daudé Feb. 16, 2023, 3:29 p.m. UTC | #3
On 16/2/23 15:23, Marc-André Lureau wrote:
> Hi Philippe
> 
> On Thu, Feb 16, 2023 at 2:14 AM Philippe Mathieu-Daudé
> <philmd@linaro.org> wrote:
>>
>> Hi Marc-André,
>>
>> [very old patch...]
>>
>> On 22/10/16 11:52, Marc-André Lureau wrote:
>>> In most cases, front ends do not care about the side effect of
>>> CharBackend, so we can simply skip the checks and call the qemu_chr_fe
>>> functions even without associated CharDriver.
>>>
>>> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>>> ---
>>>    hw/arm/pxa2xx.c           |  8 +++-----
>>>    hw/arm/strongarm.c        | 16 ++++++---------
>>>    hw/char/bcm2835_aux.c     | 18 ++++++-----------
>>>    hw/char/cadence_uart.c    | 24 +++++++---------------
>>
>>>    qemu-char.c               | 51 ++++++++++++++++++++++++++++++++++++++---------
>>>    include/sysemu/char.h     | 40 +++++++++++++++++++++++++------------
>>>    22 files changed, 156 insertions(+), 191 deletions(-)
>>
>>
>>> diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c
>>> index 4459b2d..291818e 100644
>>> --- a/hw/char/cadence_uart.c
>>> +++ b/hw/char/cadence_uart.c
>>> @@ -142,9 +142,7 @@ static void uart_rx_reset(CadenceUARTState *s)
>>>    {
>>>        s->rx_wpos = 0;
>>>        s->rx_count = 0;
>>> -    if (s->chr.chr) {
>>> -        qemu_chr_fe_accept_input(&s->chr);
>>> -    }
>>> +    qemu_chr_fe_accept_input(&s->chr);
>>
>> I'm trying to understand this change. This code comes from:
>>
>> commit 9121d02cb33c96b444a3973579f5edc119597e81
>>
>>       char/cadence_uart: Fix reset for unattached instances
>>
>>       commit 1db8b5efe0c2b5000e50691eea61264a615f43de introduced an issue
>>       where QEMU would segfault if you have an unattached Cadence UART.
>>
>>       Fix by guarding the flush-on-reset logic on there being a qemu_chr
>>       attachment.
>>
>> diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c
>> index 131370a74b..4d457f8c65 100644
>> --- a/hw/char/cadence_uart.c
>> +++ b/hw/char/cadence_uart.c
>> @@ -157,7 +157,9 @@ static void uart_rx_reset(UartState *s)
>>    {
>>        s->rx_wpos = 0;
>>        s->rx_count = 0;
>> -    qemu_chr_accept_input(s->chr);
>> +    if (s->chr) {
>> +        qemu_chr_accept_input(s->chr);
>> +    }
>>
>> When resetting the xlnx-zcu102 machine, I hit:
>>
>> (lldb) bt
>> * thread #1, queue = 'com.apple.main-thread', stop reason =
>> EXC_BAD_ACCESS (code=1, address=0x50)
>>     * frame #0: 0x10020a740 gd_vc_send_chars(vc=0x000000000) at
>> gtk.c:1759:41 [opt]
>>       frame #1: 0x100636264 qemu_chr_fe_accept_input(be=<unavailable>) at
>> char-fe.c:159:9 [opt]
>>       frame #2: 0x1000608e0 cadence_uart_reset_hold [inlined]
>> uart_rx_reset(s=0x10810a960) at cadence_uart.c:158:5 [opt]
>>       frame #3: 0x1000608d4 cadence_uart_reset_hold(obj=0x10810a960) at
>> cadence_uart.c:530:5 [opt]
>>       frame #4: 0x100580ab4 resettable_phase_hold(obj=0x10810a960,
>> opaque=0x000000000, type=<unavailable>) at resettable.c:0 [opt]
>>       frame #5: 0x10057d1b0 bus_reset_child_foreach(obj=<unavailable>,
>> cb=(resettable_phase_hold at resettable.c:162), opaque=0x000000000,
>> type=RESET_TYPE_COLD) at bus.c:97:13 [opt]
>>       frame #6: 0x1005809f8 resettable_phase_hold [inlined]
>> resettable_child_foreach(rc=0x000060000332d2c0, obj=0x0000600002c1c180,
>> cb=<unavailable>, opaque=0x000000000, type=RESET_TYPE_COLD) at
>> resettable.c:96:9 [opt]
>>       frame #7: 0x1005809d8 resettable_phase_hold(obj=0x0000600002c1c180,
>> opaque=0x000000000, type=RESET_TYPE_COLD) at resettable.c:173:5 [opt]
>>       frame #8: 0x1005803a0
>> resettable_assert_reset(obj=0x0000600002c1c180, type=<unavailable>) at
>> resettable.c:60:5 [opt]
>>       frame #9: 0x10058027c resettable_reset(obj=0x0000600002c1c180,
>> type=RESET_TYPE_COLD) at resettable.c:45:5 [opt]
>>
>> Doing similar to commit 9121d02cb3...:
>>
>> -- >8 --
>> diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c
>> index c069a30842..deadee1788 100644
>> --- a/hw/char/cadence_uart.c
>> +++ b/hw/char/cadence_uart.c
>> @@ -155,7 +155,9 @@ static void uart_rx_reset(CadenceUARTState *s)
>>    {
>>        s->rx_wpos = 0;
>>        s->rx_count = 0;
>> -    qemu_chr_fe_accept_input(&s->chr);
>> +    if (qemu_chr_fe_backend_open(&s->chr)) {
>> +        qemu_chr_fe_accept_input(&s->chr);
>> +    }
>>    }
>> ---
>>
>> ... fixes the issue but I'm not sure 1/ this is a correct use of the
>> chardev API and 2/ this is how the HW work at reset.
> 
> The trouble is that GTK/VTE console/chardev creation is done later.
> 
> I think we should rather fix ui/gtk.c, as this could happen with other
> char frontends:
> 
> diff --git a/ui/gtk.c b/ui/gtk.c
> index 4817623c8f..dfaf6d33c3 100644
> --- a/ui/gtk.c
> +++ b/ui/gtk.c
> @@ -1783,7 +1783,9 @@ static void gd_vc_chr_accept_input(Chardev *chr)
>       VCChardev *vcd = VC_CHARDEV(chr);
>       VirtualConsole *vc = vcd->console;
> 
> -    gd_vc_send_chars(vc);
> +    if (vc) {
> +        gd_vc_send_chars(vc);
> +    }

Easy :) If you send a proper patch, feel free to include:

Tested-by: Philippe Mathieu-Daudé <philmd@linaro.org>

Thanks!
diff mbox

Patch

diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c
index cd98379..c9f4503 100644
--- a/hw/arm/pxa2xx.c
+++ b/hw/arm/pxa2xx.c
@@ -1903,7 +1903,7 @@  static void pxa2xx_fir_write(void *opaque, hwaddr addr,
         } else {
             ch = ~value;
         }
-        if (s->chr.chr && s->enable && (s->control[0] & (1 << 3))) { /* TXE */
+        if (s->enable && (s->control[0] & (1 << 3))) { /* TXE */
             /* XXX this blocks entire thread. Rewrite to use
              * qemu_chr_fe_write and background I/O callbacks */
             qemu_chr_fe_write_all(&s->chr, &ch, 1);
@@ -1975,10 +1975,8 @@  static void pxa2xx_fir_realize(DeviceState *dev, Error **errp)
 {
     PXA2xxFIrState *s = PXA2XX_FIR(dev);
 
-    if (s->chr.chr) {
-        qemu_chr_fe_set_handlers(&s->chr, pxa2xx_fir_is_empty,
-                                 pxa2xx_fir_rx, pxa2xx_fir_event, s, NULL);
-    }
+    qemu_chr_fe_set_handlers(&s->chr, pxa2xx_fir_is_empty,
+                             pxa2xx_fir_rx, pxa2xx_fir_event, s, NULL);
 }
 
 static bool pxa2xx_fir_vmstate_validate(void *opaque, int version_id)
diff --git a/hw/arm/strongarm.c b/hw/arm/strongarm.c
index fd13a39..370198a 100644
--- a/hw/arm/strongarm.c
+++ b/hw/arm/strongarm.c
@@ -1020,9 +1020,7 @@  static void strongarm_uart_update_parameters(StrongARMUARTState *s)
     ssp.data_bits = data_bits;
     ssp.stop_bits = stop_bits;
     s->char_transmit_time =  (NANOSECONDS_PER_SECOND / speed) * frame_size;
-    if (s->chr.chr) {
-        qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
-    }
+    qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
 
     DPRINTF(stderr, "%s speed=%d parity=%c data=%d stop=%d\n", s->chr->label,
             speed, parity, data_bits, stop_bits);
@@ -1239,13 +1237,11 @@  static void strongarm_uart_init(Object *obj)
     s->rx_timeout_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, strongarm_uart_rx_to, s);
     s->tx_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, strongarm_uart_tx, s);
 
-    if (s->chr.chr) {
-        qemu_chr_fe_set_handlers(&s->chr,
-                                 strongarm_uart_can_receive,
-                                 strongarm_uart_receive,
-                                 strongarm_uart_event,
-                                 s, NULL);
-    }
+    qemu_chr_fe_set_handlers(&s->chr,
+                             strongarm_uart_can_receive,
+                             strongarm_uart_receive,
+                             strongarm_uart_event,
+                             s, NULL);
 }
 
 static void strongarm_uart_reset(DeviceState *dev)
diff --git a/hw/char/bcm2835_aux.c b/hw/char/bcm2835_aux.c
index c49ec8c..af329aa 100644
--- a/hw/char/bcm2835_aux.c
+++ b/hw/char/bcm2835_aux.c
@@ -79,9 +79,7 @@  static uint64_t bcm2835_aux_read(void *opaque, hwaddr offset, unsigned size)
                 s->read_pos = 0;
             }
         }
-        if (s->chr.chr) {
-            qemu_chr_fe_accept_input(&s->chr);
-        }
+        qemu_chr_fe_accept_input(&s->chr);
         bcm2835_aux_update(s);
         return c;
 
@@ -168,11 +166,9 @@  static void bcm2835_aux_write(void *opaque, hwaddr offset, uint64_t value,
     case AUX_MU_IO_REG:
         /* "DLAB bit set means access baudrate register" is NYI */
         ch = value;
-        if (s->chr.chr) {
-            /* XXX this blocks entire thread. Rewrite to use
-             * qemu_chr_fe_write and background I/O callbacks */
-            qemu_chr_fe_write_all(&s->chr, &ch, 1);
-        }
+        /* XXX this blocks entire thread. Rewrite to use
+         * qemu_chr_fe_write and background I/O callbacks */
+        qemu_chr_fe_write_all(&s->chr, &ch, 1);
         break;
 
     case AUX_MU_IER_REG:
@@ -282,10 +278,8 @@  static void bcm2835_aux_realize(DeviceState *dev, Error **errp)
 {
     BCM2835AuxState *s = BCM2835_AUX(dev);
 
-    if (s->chr.chr) {
-        qemu_chr_fe_set_handlers(&s->chr, bcm2835_aux_can_receive,
-                                 bcm2835_aux_receive, NULL, s, NULL);
-    }
+    qemu_chr_fe_set_handlers(&s->chr, bcm2835_aux_can_receive,
+                             bcm2835_aux_receive, NULL, s, NULL);
 }
 
 static Property bcm2835_aux_props[] = {
diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c
index 4459b2d..291818e 100644
--- a/hw/char/cadence_uart.c
+++ b/hw/char/cadence_uart.c
@@ -142,9 +142,7 @@  static void uart_rx_reset(CadenceUARTState *s)
 {
     s->rx_wpos = 0;
     s->rx_count = 0;
-    if (s->chr.chr) {
-        qemu_chr_fe_accept_input(&s->chr);
-    }
+    qemu_chr_fe_accept_input(&s->chr);
 }
 
 static void uart_tx_reset(CadenceUARTState *s)
@@ -156,10 +154,8 @@  static void uart_send_breaks(CadenceUARTState *s)
 {
     int break_enabled = 1;
 
-    if (s->chr.chr) {
-        qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_BREAK,
-                          &break_enabled);
-    }
+    qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_BREAK,
+                      &break_enabled);
 }
 
 static void uart_parameters_setup(CadenceUARTState *s)
@@ -210,9 +206,7 @@  static void uart_parameters_setup(CadenceUARTState *s)
 
     packet_size += ssp.data_bits + ssp.stop_bits;
     s->char_tx_time = (NANOSECONDS_PER_SECOND / ssp.speed) * packet_size;
-    if (s->chr.chr) {
-        qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
-    }
+    qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
 }
 
 static int uart_can_receive(void *opaque)
@@ -368,9 +362,7 @@  static void uart_read_rx_fifo(CadenceUARTState *s, uint32_t *c)
         *c = s->rx_fifo[rx_rpos];
         s->rx_count--;
 
-        if (s->chr.chr) {
-            qemu_chr_fe_accept_input(&s->chr);
-        }
+        qemu_chr_fe_accept_input(&s->chr);
     } else {
         *c = 0;
     }
@@ -474,10 +466,8 @@  static void cadence_uart_realize(DeviceState *dev, Error **errp)
     s->fifo_trigger_handle = timer_new_ns(QEMU_CLOCK_VIRTUAL,
                                           fifo_trigger_update, s);
 
-    if (s->chr.chr) {
-        qemu_chr_fe_set_handlers(&s->chr, uart_can_receive, uart_receive,
-                                 uart_event, s, NULL);
-    }
+    qemu_chr_fe_set_handlers(&s->chr, uart_can_receive, uart_receive,
+                             uart_event, s, NULL);
 }
 
 static void cadence_uart_init(Object *obj)
diff --git a/hw/char/digic-uart.c b/hw/char/digic-uart.c
index c7b3db6..2955e19 100644
--- a/hw/char/digic-uart.c
+++ b/hw/char/digic-uart.c
@@ -76,11 +76,9 @@  static void digic_uart_write(void *opaque, hwaddr addr, uint64_t value,
 
     switch (addr) {
     case R_TX:
-        if (s->chr.chr) {
-            /* XXX this blocks entire thread. Rewrite to use
-             * qemu_chr_fe_write and background I/O callbacks */
-            qemu_chr_fe_write_all(&s->chr, &ch, 1);
-        }
+        /* XXX this blocks entire thread. Rewrite to use
+         * qemu_chr_fe_write and background I/O callbacks */
+        qemu_chr_fe_write_all(&s->chr, &ch, 1);
         break;
 
     case R_ST:
@@ -147,10 +145,8 @@  static void digic_uart_realize(DeviceState *dev, Error **errp)
 {
     DigicUartState *s = DIGIC_UART(dev);
 
-    if (s->chr.chr) {
-        qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx,
-                                 uart_event, s, NULL);
-    }
+    qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx,
+                             uart_event, s, NULL);
 }
 
 static void digic_uart_init(Object *obj)
diff --git a/hw/char/escc.c b/hw/char/escc.c
index 4578a46..c71b0a8 100644
--- a/hw/char/escc.c
+++ b/hw/char/escc.c
@@ -599,9 +599,7 @@  static uint64_t escc_mem_read(void *opaque, hwaddr addr,
         else
             ret = s->rx;
         trace_escc_mem_readb_data(CHN_C(s), ret);
-        if (s->chr.chr) {
-            qemu_chr_fe_accept_input(&s->chr);
-        }
+        qemu_chr_fe_accept_input(&s->chr);
         return ret;
     default:
         break;
diff --git a/hw/char/etraxfs_ser.c b/hw/char/etraxfs_ser.c
index d812954..18c374b 100644
--- a/hw/char/etraxfs_ser.c
+++ b/hw/char/etraxfs_ser.c
@@ -231,11 +231,9 @@  static void etraxfs_ser_realize(DeviceState *dev, Error **errp)
 {
     ETRAXSerial *s = ETRAX_SERIAL(dev);
 
-    if (s->chr.chr) {
-        qemu_chr_fe_set_handlers(&s->chr,
-                                 serial_can_receive, serial_receive,
-                                 serial_event, s, NULL);
-    }
+    qemu_chr_fe_set_handlers(&s->chr,
+                             serial_can_receive, serial_receive,
+                             serial_event, s, NULL);
 }
 
 static void etraxfs_ser_class_init(ObjectClass *klass, void *data)
diff --git a/hw/char/imx_serial.c b/hw/char/imx_serial.c
index d9a0a25..8dbb7b2 100644
--- a/hw/char/imx_serial.c
+++ b/hw/char/imx_serial.c
@@ -121,9 +121,7 @@  static uint64_t imx_serial_read(void *opaque, hwaddr offset,
             s->usr2 &= ~USR2_RDR;
             s->uts1 |= UTS1_RXEMPTY;
             imx_update(s);
-            if (s->chr.chr) {
-                qemu_chr_fe_accept_input(&s->chr);
-            }
+            qemu_chr_fe_accept_input(&s->chr);
         }
         return c;
 
@@ -182,11 +180,9 @@  static void imx_serial_write(void *opaque, hwaddr offset,
     case 0x10: /* UTXD */
         ch = value;
         if (s->ucr2 & UCR2_TXEN) {
-            if (s->chr.chr) {
-                /* XXX this blocks entire thread. Rewrite to use
-                 * qemu_chr_fe_write and background I/O callbacks */
-                qemu_chr_fe_write_all(&s->chr, &ch, 1);
-            }
+            /* XXX this blocks entire thread. Rewrite to use
+             * qemu_chr_fe_write and background I/O callbacks */
+            qemu_chr_fe_write_all(&s->chr, &ch, 1);
             s->usr1 &= ~USR1_TRDY;
             imx_update(s);
             s->usr1 |= USR1_TRDY;
@@ -215,9 +211,7 @@  static void imx_serial_write(void *opaque, hwaddr offset,
         }
         if (value & UCR2_RXEN) {
             if (!(s->ucr2 & UCR2_RXEN)) {
-                if (s->chr.chr) {
-                    qemu_chr_fe_accept_input(&s->chr);
-                }
+                qemu_chr_fe_accept_input(&s->chr);
             }
         }
         s->ucr2 = value & 0xffff;
@@ -319,12 +313,10 @@  static void imx_serial_realize(DeviceState *dev, Error **errp)
 {
     IMXSerialState *s = IMX_SERIAL(dev);
 
-    if (s->chr.chr) {
-        qemu_chr_fe_set_handlers(&s->chr, imx_can_receive, imx_receive,
-                                 imx_event, s, NULL);
-    } else {
-        DPRINTF("No char dev for uart\n");
-    }
+    DPRINTF("char dev for uart: %p\n", qemu_chr_fe_get_driver(&s->chr));
+
+    qemu_chr_fe_set_handlers(&s->chr, imx_can_receive, imx_receive,
+                             imx_event, s, NULL);
 }
 
 static void imx_serial_init(Object *obj)
diff --git a/hw/char/ipoctal232.c b/hw/char/ipoctal232.c
index d504721..6f150e0 100644
--- a/hw/char/ipoctal232.c
+++ b/hw/char/ipoctal232.c
@@ -288,9 +288,7 @@  static uint16_t io_read(IPackDevice *ip, uint8_t addr)
             if (ch->rx_pending == 0) {
                 ch->sr &= ~SR_RXRDY;
                 blk->isr &= ~ISR_RXRDY(channel);
-                if (ch->dev.chr) {
-                    qemu_chr_fe_accept_input(&ch->dev);
-                }
+                qemu_chr_fe_accept_input(&ch->dev);
             } else {
                 ch->rhr_idx = (ch->rhr_idx + 1) % RX_FIFO_SIZE;
             }
@@ -357,13 +355,11 @@  static void io_write(IPackDevice *ip, uint8_t addr, uint16_t val)
     case REG_THRa:
     case REG_THRb:
         if (ch->sr & SR_TXRDY) {
+            uint8_t thr = reg;
             DPRINTF("Write THR%c (0x%x)\n", channel + 'a', reg);
-            if (ch->dev.chr) {
-                uint8_t thr = reg;
-                /* XXX this blocks entire thread. Rewrite to use
-                 * qemu_chr_fe_write and background I/O callbacks */
-                qemu_chr_fe_write_all(&ch->dev, &thr, 1);
-            }
+            /* XXX this blocks entire thread. Rewrite to use
+             * qemu_chr_fe_write and background I/O callbacks */
+            qemu_chr_fe_write_all(&ch->dev, &thr, 1);
         } else {
             DPRINTF("Write THR%c (0x%x), Tx disabled\n", channel + 'a', reg);
         }
diff --git a/hw/char/lm32_juart.c b/hw/char/lm32_juart.c
index 9629e9e..febb412 100644
--- a/hw/char/lm32_juart.c
+++ b/hw/char/lm32_juart.c
@@ -75,11 +75,9 @@  void lm32_juart_set_jtx(DeviceState *d, uint32_t jtx)
     trace_lm32_juart_set_jtx(s->jtx);
 
     s->jtx = jtx;
-    if (s->chr.chr) {
-        /* XXX this blocks entire thread. Rewrite to use
-         * qemu_chr_fe_write and background I/O callbacks */
-        qemu_chr_fe_write_all(&s->chr, &ch, 1);
-    }
+    /* XXX this blocks entire thread. Rewrite to use
+     * qemu_chr_fe_write and background I/O callbacks */
+    qemu_chr_fe_write_all(&s->chr, &ch, 1);
 }
 
 void lm32_juart_set_jrx(DeviceState *d, uint32_t jtx)
@@ -120,10 +118,8 @@  static void lm32_juart_realize(DeviceState *dev, Error **errp)
 {
     LM32JuartState *s = LM32_JUART(dev);
 
-    if (s->chr.chr) {
-        qemu_chr_fe_set_handlers(&s->chr, juart_can_rx, juart_rx,
-                                 juart_event, s, NULL);
-    }
+    qemu_chr_fe_set_handlers(&s->chr, juart_can_rx, juart_rx,
+                             juart_event, s, NULL);
 }
 
 static const VMStateDescription vmstate_lm32_juart = {
diff --git a/hw/char/lm32_uart.c b/hw/char/lm32_uart.c
index e325b91..1b2746f 100644
--- a/hw/char/lm32_uart.c
+++ b/hw/char/lm32_uart.c
@@ -177,11 +177,9 @@  static void uart_write(void *opaque, hwaddr addr,
     addr >>= 2;
     switch (addr) {
     case R_RXTX:
-        if (s->chr.chr) {
-            /* XXX this blocks entire thread. Rewrite to use
-             * qemu_chr_fe_write and background I/O callbacks */
-            qemu_chr_fe_write_all(&s->chr, &ch, 1);
-        }
+        /* XXX this blocks entire thread. Rewrite to use
+         * qemu_chr_fe_write and background I/O callbacks */
+        qemu_chr_fe_write_all(&s->chr, &ch, 1);
         break;
     case R_IER:
     case R_LCR:
@@ -267,10 +265,8 @@  static void lm32_uart_realize(DeviceState *dev, Error **errp)
 {
     LM32UartState *s = LM32_UART(dev);
 
-    if (s->chr.chr) {
-        qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx,
-                                 uart_event, s, NULL);
-    }
+    qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx,
+                             uart_event, s, NULL);
 }
 
 static const VMStateDescription vmstate_lm32_uart = {
diff --git a/hw/char/mcf_uart.c b/hw/char/mcf_uart.c
index b505343..456591a 100644
--- a/hw/char/mcf_uart.c
+++ b/hw/char/mcf_uart.c
@@ -114,11 +114,9 @@  uint64_t mcf_uart_read(void *opaque, hwaddr addr,
 static void mcf_uart_do_tx(mcf_uart_state *s)
 {
     if (s->tx_enabled && (s->sr & MCF_UART_TxEMP) == 0) {
-        if (s->chr.chr) {
-            /* XXX this blocks entire thread. Rewrite to use
-             * qemu_chr_fe_write and background I/O callbacks */
-            qemu_chr_fe_write_all(&s->chr, (unsigned char *)&s->tb, 1);
-        }
+        /* XXX this blocks entire thread. Rewrite to use
+         * qemu_chr_fe_write and background I/O callbacks */
+        qemu_chr_fe_write_all(&s->chr, (unsigned char *)&s->tb, 1);
         s->sr |= MCF_UART_TxEMP;
     }
     if (s->tx_enabled) {
diff --git a/hw/char/milkymist-uart.c b/hw/char/milkymist-uart.c
index 0a4c617..dec0a8c 100644
--- a/hw/char/milkymist-uart.c
+++ b/hw/char/milkymist-uart.c
@@ -124,9 +124,7 @@  static void uart_write(void *opaque, hwaddr addr, uint64_t value,
     addr >>= 2;
     switch (addr) {
     case R_RXTX:
-        if (s->chr.chr) {
-            qemu_chr_fe_write_all(&s->chr, &ch, 1);
-        }
+        qemu_chr_fe_write_all(&s->chr, &ch, 1);
         s->regs[R_STAT] |= STAT_TX_EVT;
         break;
     case R_DIV:
@@ -200,10 +198,8 @@  static void milkymist_uart_realize(DeviceState *dev, Error **errp)
 {
     MilkymistUartState *s = MILKYMIST_UART(dev);
 
-    if (s->chr.chr) {
-        qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx,
-                                 uart_event, s, NULL);
-    }
+    qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx,
+                             uart_event, s, NULL);
 }
 
 static void milkymist_uart_init(Object *obj)
diff --git a/hw/char/pl011.c b/hw/char/pl011.c
index 52ec866..900ee5d 100644
--- a/hw/char/pl011.c
+++ b/hw/char/pl011.c
@@ -87,9 +87,7 @@  static uint64_t pl011_read(void *opaque, hwaddr offset,
         trace_pl011_read_fifo(s->read_count);
         s->rsr = c >> 8;
         pl011_update(s);
-        if (s->chr.chr) {
-            qemu_chr_fe_accept_input(&s->chr);
-        }
+        qemu_chr_fe_accept_input(&s->chr);
         r = c;
         break;
     case 1: /* UARTRSR */
@@ -168,11 +166,9 @@  static void pl011_write(void *opaque, hwaddr offset,
     case 0: /* UARTDR */
         /* ??? Check if transmitter is enabled.  */
         ch = value;
-        if (s->chr.chr) {
-            /* XXX this blocks entire thread. Rewrite to use
-             * qemu_chr_fe_write and background I/O callbacks */
-            qemu_chr_fe_write_all(&s->chr, &ch, 1);
-        }
+        /* XXX this blocks entire thread. Rewrite to use
+         * qemu_chr_fe_write and background I/O callbacks */
+        qemu_chr_fe_write_all(&s->chr, &ch, 1);
         s->int_level |= PL011_INT_TX;
         pl011_update(s);
         break;
@@ -332,10 +328,8 @@  static void pl011_realize(DeviceState *dev, Error **errp)
 {
     PL011State *s = PL011(dev);
 
-    if (s->chr.chr) {
-        qemu_chr_fe_set_handlers(&s->chr, pl011_can_receive, pl011_receive,
-                                 pl011_event, s, NULL);
-    }
+    qemu_chr_fe_set_handlers(&s->chr, pl011_can_receive, pl011_receive,
+                             pl011_event, s, NULL);
 }
 
 static void pl011_class_init(ObjectClass *oc, void *data)
diff --git a/hw/char/sclpconsole-lm.c b/hw/char/sclpconsole-lm.c
index 0660cbc..7191717 100644
--- a/hw/char/sclpconsole-lm.c
+++ b/hw/char/sclpconsole-lm.c
@@ -312,10 +312,8 @@  static int console_init(SCLPEvent *event)
     }
     console_available = true;
 
-    if (scon->chr.chr) {
-        qemu_chr_fe_set_handlers(&scon->chr, chr_can_read,
-                                 chr_read, NULL, scon, NULL);
-    }
+    qemu_chr_fe_set_handlers(&scon->chr, chr_can_read,
+                             chr_read, NULL, scon, NULL);
 
     return 0;
 }
diff --git a/hw/char/sclpconsole.c b/hw/char/sclpconsole.c
index 0559208..27a6034 100644
--- a/hw/char/sclpconsole.c
+++ b/hw/char/sclpconsole.c
@@ -227,10 +227,8 @@  static int console_init(SCLPEvent *event)
         return -1;
     }
     console_available = true;
-    if (scon->chr.chr) {
-        qemu_chr_fe_set_handlers(&scon->chr, chr_can_read,
-                                 chr_read, NULL, scon, NULL);
-    }
+    qemu_chr_fe_set_handlers(&scon->chr, chr_can_read,
+                             chr_read, NULL, scon, NULL);
 
     return 0;
 }
diff --git a/hw/char/stm32f2xx_usart.c b/hw/char/stm32f2xx_usart.c
index 03ccc52..8e9852b 100644
--- a/hw/char/stm32f2xx_usart.c
+++ b/hw/char/stm32f2xx_usart.c
@@ -97,17 +97,13 @@  static uint64_t stm32f2xx_usart_read(void *opaque, hwaddr addr,
     case USART_SR:
         retvalue = s->usart_sr;
         s->usart_sr &= ~USART_SR_TC;
-        if (s->chr.chr) {
-            qemu_chr_fe_accept_input(&s->chr);
-        }
+        qemu_chr_fe_accept_input(&s->chr);
         return retvalue;
     case USART_DR:
         DB_PRINT("Value: 0x%" PRIx32 ", %c\n", s->usart_dr, (char) s->usart_dr);
         s->usart_sr |= USART_SR_TXE;
         s->usart_sr &= ~USART_SR_RXNE;
-        if (s->chr.chr) {
-            qemu_chr_fe_accept_input(&s->chr);
-        }
+        qemu_chr_fe_accept_input(&s->chr);
         qemu_set_irq(s->irq, 0);
         return s->usart_dr & 0x3FF;
     case USART_BRR:
@@ -152,11 +148,9 @@  static void stm32f2xx_usart_write(void *opaque, hwaddr addr,
     case USART_DR:
         if (value < 0xF000) {
             ch = value;
-            if (s->chr.chr) {
-                /* XXX this blocks entire thread. Rewrite to use
-                 * qemu_chr_fe_write and background I/O callbacks */
-                qemu_chr_fe_write_all(&s->chr, &ch, 1);
-            }
+            /* XXX this blocks entire thread. Rewrite to use
+             * qemu_chr_fe_write and background I/O callbacks */
+            qemu_chr_fe_write_all(&s->chr, &ch, 1);
             s->usart_sr |= USART_SR_TC;
             s->usart_sr &= ~USART_SR_TXE;
         }
@@ -212,10 +206,8 @@  static void stm32f2xx_usart_realize(DeviceState *dev, Error **errp)
 {
     STM32F2XXUsartState *s = STM32F2XX_USART(dev);
 
-    if (s->chr.chr) {
-        qemu_chr_fe_set_handlers(&s->chr, stm32f2xx_usart_can_receive,
-                                 stm32f2xx_usart_receive, NULL, s, NULL);
-    }
+    qemu_chr_fe_set_handlers(&s->chr, stm32f2xx_usart_can_receive,
+                             stm32f2xx_usart_receive, NULL, s, NULL);
 }
 
 static void stm32f2xx_usart_class_init(ObjectClass *klass, void *data)
diff --git a/hw/char/virtio-console.c b/hw/char/virtio-console.c
index 135f589..378c1c1 100644
--- a/hw/char/virtio-console.c
+++ b/hw/char/virtio-console.c
@@ -108,7 +108,7 @@  static void set_guest_connected(VirtIOSerialPort *port, int guest_connected)
     DeviceState *dev = DEVICE(port);
     VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_GET_CLASS(port);
 
-    if (vcon->chr.chr && !k->is_console) {
+    if (!k->is_console) {
         qemu_chr_fe_set_open(&vcon->chr, guest_connected);
     }
 
@@ -122,9 +122,7 @@  static void guest_writable(VirtIOSerialPort *port)
 {
     VirtConsole *vcon = VIRTIO_CONSOLE(port);
 
-    if (vcon->chr.chr) {
-        qemu_chr_fe_accept_input(&vcon->chr);
-    }
+    qemu_chr_fe_accept_input(&vcon->chr);
 }
 
 /* Readiness of the guest to accept data on a port */
diff --git a/hw/char/xen_console.c b/hw/char/xen_console.c
index f664366..785bf86 100644
--- a/hw/char/xen_console.c
+++ b/hw/char/xen_console.c
@@ -244,10 +244,8 @@  static int con_initialise(struct XenDevice *xendev)
 	return -1;
 
     xen_be_bind_evtchn(&con->xendev);
-    if (con->chr.chr) {
-        qemu_chr_fe_set_handlers(&con->chr, xencons_can_receive,
-                                 xencons_receive, NULL, con, NULL);
-    }
+    qemu_chr_fe_set_handlers(&con->chr, xencons_can_receive,
+                             xencons_receive, NULL, con, NULL);
 
     xen_be_printf(xendev, 1, "ring mfn %d, remote port %d, local port %d, limit %zd\n",
 		  con->ring_ref,
diff --git a/hw/char/xilinx_uartlite.c b/hw/char/xilinx_uartlite.c
index d6df643..c7888f7 100644
--- a/hw/char/xilinx_uartlite.c
+++ b/hw/char/xilinx_uartlite.c
@@ -143,11 +143,9 @@  uart_write(void *opaque, hwaddr addr,
             break;
 
         case R_TX:
-            if (s->chr.chr) {
-                /* XXX this blocks entire thread. Rewrite to use
-                 * qemu_chr_fe_write and background I/O callbacks */
-                qemu_chr_fe_write_all(&s->chr, &ch, 1);
-            }
+            /* XXX this blocks entire thread. Rewrite to use
+             * qemu_chr_fe_write and background I/O callbacks */
+            qemu_chr_fe_write_all(&s->chr, &ch, 1);
             s->regs[addr] = value;
 
             /* hax.  */
@@ -213,10 +211,8 @@  static void xilinx_uartlite_realize(DeviceState *dev, Error **errp)
 {
     XilinxUARTLite *s = XILINX_UARTLITE(dev);
 
-    if (s->chr.chr) {
-        qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx,
-                                 uart_event, s, NULL);
-    }
+    qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx,
+                             uart_event, s, NULL);
 }
 
 static void xilinx_uartlite_init(Object *obj)
diff --git a/qemu-char.c b/qemu-char.c
index 40c5b50..dee2ced 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -273,6 +273,10 @@  int qemu_chr_fe_write(CharBackend *be, const uint8_t *buf, int len)
     CharDriverState *s = be->chr;
     int ret;
 
+    if (!s) {
+        return 0;
+    }
+
     if (s->replay && replay_mode == REPLAY_MODE_PLAY) {
         int offset;
         replay_char_write_event_load(&ret, &offset);
@@ -325,6 +329,10 @@  int qemu_chr_fe_write_all(CharBackend *be, const uint8_t *buf, int len)
 {
     CharDriverState *s = be->chr;
 
+    if (!s) {
+        return 0;
+    }
+
     return qemu_chr_write_all(s, buf, len);
 }
 
@@ -334,10 +342,10 @@  int qemu_chr_fe_read_all(CharBackend *be, uint8_t *buf, int len)
     int offset = 0, counter = 10;
     int res;
 
-    if (!s->chr_sync_read) {
+    if (!s || !s->chr_sync_read) {
         return 0;
     }
-    
+
     if (s->replay && replay_mode == REPLAY_MODE_PLAY) {
         return replay_char_read_all_load(buf);
     }
@@ -378,7 +386,8 @@  int qemu_chr_fe_ioctl(CharBackend *be, int cmd, void *arg)
 {
     CharDriverState *s = be->chr;
     int res;
-    if (!s->chr_ioctl || s->replay) {
+
+    if (!s || !s->chr_ioctl || s->replay) {
         res = -ENOTSUP;
     } else {
         res = s->chr_ioctl(s, cmd, arg);
@@ -418,7 +427,7 @@  int qemu_chr_fe_get_msgfd(CharBackend *be)
     CharDriverState *s = be->chr;
     int fd;
     int res = (qemu_chr_fe_get_msgfds(be, &fd, 1) == 1) ? fd : -1;
-    if (s->replay) {
+    if (s && s->replay) {
         fprintf(stderr,
                 "Replay: get msgfd is not supported for serial devices yet\n");
         exit(1);
@@ -430,6 +439,10 @@  int qemu_chr_fe_get_msgfds(CharBackend *be, int *fds, int len)
 {
     CharDriverState *s = be->chr;
 
+    if (!s) {
+        return -1;
+    }
+
     return s->get_msgfds ? s->get_msgfds(s, fds, len) : -1;
 }
 
@@ -437,6 +450,10 @@  int qemu_chr_fe_set_msgfds(CharBackend *be, int *fds, int num)
 {
     CharDriverState *s = be->chr;
 
+    if (!s) {
+        return -1;
+    }
+
     return s->set_msgfds ? s->set_msgfds(s, fds, num) : -1;
 }
 
@@ -449,6 +466,10 @@  void qemu_chr_fe_accept_input(CharBackend *be)
 {
     CharDriverState *s = be->chr;
 
+    if (!s) {
+        return;
+    }
+
     if (s->chr_accept_input)
         s->chr_accept_input(s);
     qemu_notify_event();
@@ -959,7 +980,10 @@  void qemu_chr_fe_set_handlers(CharBackend *b,
 void qemu_chr_fe_take_focus(CharBackend *b)
 {
     assert(b);
-    assert(b->chr);
+
+    if (!b->chr) {
+        return;
+    }
 
     if (b->chr->is_mux) {
         mux_set_focus(b->chr->opaque, b->tag);
@@ -3363,6 +3387,11 @@  static int qemu_chr_wait_connected(CharDriverState *chr, Error **errp)
 
 int qemu_chr_fe_wait_connected(CharBackend *be, Error **errp)
 {
+    if (!be->chr) {
+        error_setg(errp, "missing associated backend");
+        return -1;
+    }
+
     return qemu_chr_wait_connected(be->chr, errp);
 }
 
@@ -4167,7 +4196,7 @@  void qemu_chr_fe_set_echo(CharBackend *be, bool echo)
 {
     CharDriverState *chr = be->chr;
 
-    if (chr->chr_set_echo) {
+    if (chr && chr->chr_set_echo) {
         chr->chr_set_echo(chr, echo);
     }
 }
@@ -4176,6 +4205,10 @@  void qemu_chr_fe_set_open(CharBackend *be, int fe_open)
 {
     CharDriverState *chr = be->chr;
 
+    if (!chr) {
+        return;
+    }
+
     if (chr->fe_open == fe_open) {
         return;
     }
@@ -4189,7 +4222,7 @@  void qemu_chr_fe_event(CharBackend *be, int event)
 {
     CharDriverState *chr = be->chr;
 
-    if (chr->chr_fe_event) {
+    if (chr && chr->chr_fe_event) {
         chr->chr_fe_event(chr, event);
     }
 }
@@ -4201,7 +4234,7 @@  guint qemu_chr_fe_add_watch(CharBackend *be, GIOCondition cond,
     GSource *src;
     guint tag;
 
-    if (s->chr_add_watch == NULL) {
+    if (!s || s->chr_add_watch == NULL) {
         return 0;
     }
 
@@ -4221,7 +4254,7 @@  void qemu_chr_fe_disconnect(CharBackend *be)
 {
     CharDriverState *chr = be->chr;
 
-    if (chr->chr_disconnect) {
+    if (chr && chr->chr_disconnect) {
         chr->chr_disconnect(chr);
     }
 }
diff --git a/include/sysemu/char.h b/include/sysemu/char.h
index b81dbcc..2f60a10 100644
--- a/include/sysemu/char.h
+++ b/include/sysemu/char.h
@@ -168,6 +168,7 @@  CharDriverState *qemu_chr_new(const char *label, const char *filename);
  * @qemu_chr_fe_disconnect:
  *
  * Close a fd accpeted by character backend.
+ * Without associated CharDriver, do nothing.
  */
 void qemu_chr_fe_disconnect(CharBackend *be);
 
@@ -181,7 +182,8 @@  void qemu_chr_cleanup(void);
 /**
  * @qemu_chr_fe_wait_connected:
  *
- * Wait for characted backend to be connected.
+ * Wait for characted backend to be connected, return < 0 on error or
+ * if no assicated CharDriver.
  */
 int qemu_chr_fe_wait_connected(CharBackend *be, Error **errp);
 
@@ -220,6 +222,7 @@  void qemu_chr_free(CharDriverState *chr);
  * Ask the backend to override its normal echo setting.  This only really
  * applies to the stdio backend and is used by the QMP server such that you
  * can see what you type if you try to type QMP commands.
+ * Without associated CharDriver, do nothing.
  *
  * @echo true to enable echo, false to disable echo
  */
@@ -230,13 +233,15 @@  void qemu_chr_fe_set_echo(CharBackend *be, bool echo);
  *
  * Set character frontend open status.  This is an indication that the
  * front end is ready (or not) to begin doing I/O.
+ * Without associated CharDriver, do nothing.
  */
 void qemu_chr_fe_set_open(CharBackend *be, int fe_open);
 
 /**
  * @qemu_chr_fe_event:
  *
- * Send an event from the front end to the back end.
+ * Send an event from the front end to the back end. It does nothing
+ * without associated CharDriver.
  *
  * @event the event to send
  */
@@ -245,8 +250,9 @@  void qemu_chr_fe_event(CharBackend *be, int event);
 /**
  * @qemu_chr_fe_printf:
  *
- * Write to a character backend using a printf style interface.
- * This function is thread-safe.
+ * Write to a character backend using a printf style interface.  This
+ * function is thread-safe. It does nothing without associated
+ * CharDriver.
  *
  * @fmt see #printf
  */
@@ -259,7 +265,7 @@  void qemu_chr_fe_printf(CharBackend *be, const char *fmt, ...)
  * If the backend is connected, create and add a #GSource that fires
  * when the given condition (typically G_IO_OUT|G_IO_HUP or G_IO_HUP)
  * is active; return the #GSource's tag.  If it is disconnected,
- * return 0.
+ * or without associated CharDriver, return 0.
  *
  * @cond the condition to poll for
  * @func the function to call when the condition happens
@@ -278,7 +284,7 @@  guint qemu_chr_fe_add_watch(CharBackend *be, GIOCondition cond,
  * @buf the data
  * @len the number of bytes to send
  *
- * Returns: the number of bytes consumed
+ * Returns: the number of bytes consumed (0 if no assicated CharDriver)
  */
 int qemu_chr_fe_write(CharBackend *be, const uint8_t *buf, int len);
 
@@ -293,7 +299,7 @@  int qemu_chr_fe_write(CharBackend *be, const uint8_t *buf, int len);
  * @buf the data
  * @len the number of bytes to send
  *
- * Returns: the number of bytes consumed
+ * Returns: the number of bytes consumed (0 if no assicated CharDriver)
  */
 int qemu_chr_fe_write_all(CharBackend *be, const uint8_t *buf, int len);
 
@@ -305,7 +311,7 @@  int qemu_chr_fe_write_all(CharBackend *be, const uint8_t *buf, int len);
  * @buf the data buffer
  * @len the number of bytes to read
  *
- * Returns: the number of bytes read
+ * Returns: the number of bytes read (0 if no assicated CharDriver)
  */
 int qemu_chr_fe_read_all(CharBackend *be, uint8_t *buf, int len);
 
@@ -317,8 +323,9 @@  int qemu_chr_fe_read_all(CharBackend *be, uint8_t *buf, int len);
  * @cmd see CHR_IOCTL_*
  * @arg the data associated with @cmd
  *
- * Returns: if @cmd is not supported by the backend, -ENOTSUP, otherwise the
- *          return value depends on the semantics of @cmd
+ * Returns: if @cmd is not supported by the backend or there is no
+ *          associated CharDriver, -ENOTSUP, otherwise the return
+ *          value depends on the semantics of @cmd
  */
 int qemu_chr_fe_ioctl(CharBackend *be, int cmd, void *arg);
 
@@ -357,7 +364,7 @@  int qemu_chr_fe_get_msgfds(CharBackend *be, int *fds, int num);
  * result in overwriting the fd array with the new value without being send.
  * Upon writing the message the fd array is freed.
  *
- * Returns: -1 if fd passing isn't supported.
+ * Returns: -1 if fd passing isn't supported or no associated CharDriver.
  */
 int qemu_chr_fe_set_msgfds(CharBackend *be, int *fds, int num);
 
@@ -418,7 +425,8 @@  bool qemu_chr_fe_init(CharBackend *b, CharDriverState *s, Error **errp);
 /**
  * @qemu_chr_fe_get_driver:
  *
- * Returns the driver associated with a CharBackend or NULL.
+ * Returns the driver associated with a CharBackend or NULL if no
+ * associated CharDriver.
  */
 CharDriverState *qemu_chr_fe_get_driver(CharBackend *be);
 
@@ -426,6 +434,8 @@  CharDriverState *qemu_chr_fe_get_driver(CharBackend *be);
  * @qemu_chr_fe_deinit:
  *
  * Dissociate the CharBackend from the CharDriver.
+ *
+ * Safe to call without associated CharDriver.
  */
 void qemu_chr_fe_deinit(CharBackend *b);
 
@@ -441,6 +451,8 @@  void qemu_chr_fe_deinit(CharBackend *b);
  *
  * Set the front end char handlers. The front end takes the focus if
  * any of the handler is non-NULL.
+ *
+ * Without associated CharDriver, nothing is changed.
  */
 void qemu_chr_fe_set_handlers(CharBackend *b,
                               IOCanReadHandler *fd_can_read,
@@ -452,7 +464,9 @@  void qemu_chr_fe_set_handlers(CharBackend *b,
 /**
  * @qemu_chr_fe_take_focus:
  *
- * Take the focus (if the front end is muxed)
+ * Take the focus (if the front end is muxed).
+ *
+ * Without associated CharDriver, nothing is changed.
  */
 void qemu_chr_fe_take_focus(CharBackend *b);