From: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@futurfusion.io>
Currently, when Incus issues "chardev-change" QMP command to change
chardev backend from ringbuf to socket it receives an error (with aarch64 VM):
"Chardev user does not support chardev hotswap" [1], [2]
Let's fix this by properly implementing BackendChangeHandler for pl011.
Please, note that we have to "replay" CHR_IOCTL_SERIAL_SET_BREAK, because
if BRK bit was set before backend change (i.e. (s->lcr & LCR_BRK) is true),
then after change we need to send break to a new backend too.
Link: https://discuss.linuxcontainers.org/t/unable-to-connect-to-vm-console-on-arm-architecture/23096/3 [1]
Link: https://github.com/lxc/distrobuilder/issues/892 [2]
Reported-by: Stéphane Graber <stgraber@stgraber.org>
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
Signed-off-by: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@futurfusion.io>
---
v4:
- changed pl011_set_break() signature
[ as suggested by Peter Maydell ]
v3:
- introduced pl011_set_handlers()
[ as suggested by Alex Bennée ]
- introduced pl011_set_break()
[ as suggested by Philippe Mathieu-Daudé ]
v2:
- fixed a typo in commit author name
[ I did `git format-patch` and copied this patch from my Raspberry PI
dev/test machine and it turns out that I have a stupid typo in my
`git config get user.name` on that machine. ]
- added RWB tag from Alex Bennée
- adjusted a commit message
---
hw/char/pl011.c | 31 ++++++++++++++++++++++++++-----
1 file changed, 26 insertions(+), 5 deletions(-)
diff --git a/hw/char/pl011.c b/hw/char/pl011.c
index cb12c3e224f..5ad9e7358d5 100644
--- a/hw/char/pl011.c
+++ b/hw/char/pl011.c
@@ -425,6 +425,11 @@ static void pl011_loopback_break(PL011State *s, int brk_enable)
}
}
+static inline void pl011_set_break(PL011State *s, int brk_enable)
+{
+ qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_BREAK, &brk_enable);
+}
+
static void pl011_write(void *opaque, hwaddr offset,
uint64_t value, unsigned size)
{
@@ -462,9 +467,8 @@ static void pl011_write(void *opaque, hwaddr offset,
pl011_reset_tx_fifo(s);
}
if ((s->lcr ^ value) & LCR_BRK) {
- int break_enable = value & LCR_BRK;
- qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_BREAK,
- &break_enable);
+ bool break_enable = value & LCR_BRK;
+ pl011_set_break(s, break_enable);
pl011_loopback_break(s, break_enable);
}
s->lcr = value;
@@ -660,12 +664,29 @@ static void pl011_init(Object *obj)
s->id = pl011_id_arm;
}
+static int pl011_be_change(void *opaque);
+
+static inline void pl011_set_handlers(PL011State *s)
+{
+ qemu_chr_fe_set_handlers(&s->chr, pl011_can_receive, pl011_receive,
+ pl011_event, pl011_be_change, s, NULL, true);
+}
+
+static int pl011_be_change(void *opaque)
+{
+ PL011State *s = opaque;
+
+ pl011_set_handlers(s);
+ pl011_set_break(s, s->lcr & LCR_BRK);
+
+ return 0;
+}
+
static void pl011_realize(DeviceState *dev, Error **errp)
{
PL011State *s = PL011(dev);
- qemu_chr_fe_set_handlers(&s->chr, pl011_can_receive, pl011_receive,
- pl011_event, NULL, s, NULL, true);
+ pl011_set_handlers(s);
}
static void pl011_reset(DeviceState *dev)
--
2.47.3