[PATCH] chardev/msmouse: save and restore device state for migration and savevm

Trieu Huynh posted 1 patch 3 days, 1 hour ago
Patches applied successfully (tree, apply log)
git fetch https://github.com/patchew-project/qemu tags/patchew/20260418115230.17809-1-viking4@gmail.com
Maintainers: "Marc-André Lureau" <marcandre.lureau@redhat.com>, Paolo Bonzini <pbonzini@redhat.com>
chardev/msmouse.c | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
[PATCH] chardev/msmouse: save and restore device state for migration and savevm
Posted by Trieu Huynh 3 days, 1 hour ago
From: Trieu Huynh <vikingtc4@gmail.com>

MouseChardev holds state that must survive migration and savevm/loadvm:

  tiocm   - serial line control (RTS/DTR).  When zero the mouse is
            considered powered-off and all input events are silently
            dropped.  Losing this field after migration leaves the
            mouse in a dead state: the guest driver must toggle
            RTS/DTR again to re-detect the mouse, which most drivers
            (Linux sermouse, DOS ctmouse) only do at initialisation.

  axis[]  - accumulated X/Y movement not yet encoded into a packet.

  btns[]  - current button state.

  btnc[]  - per-button change flags used for middle-button tracking.

  outbuf  - Fifo8 of encoded mouse bytes waiting to be delivered to
            the serial port receive FIFO.

Add a VMStateDescription covering all five fields, registering it in
msmouse_chr_open and unregistering it in char_msmouse_finalize.  This
follows the same pattern used by ui/vdagent.c

Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1234

Signed-off-by: Trieu Huynh <vikingtc4@gmail.com>
---
Steps to reproduce:

* 1. Starting source QEMU under GDB
gdb -q ./qemu-system-x86_64

* Inside GDB:

(gdb) set args -chardev msmouse,id=mouse0 \
  -device isa-serial,chardev=mouse0 \
  - <other_options>

(gdb) break msmouse_input_sync
(gdb) run

* 2. Starting destination QEMU under GDB
gdb -q ./qemu-system-x86_64

* Inside GDB:

(gdb) set args -chardev msmouse,id=mouse0 \
  -S -incoming tcp:0:4444
  -device isa-serial,chardev=mouse0 \
  - <other_options>

(gdb) break msmouse_chr_ioctl
(gdb) run
<waiting for migration>

* 3. Send mouse events to source
{"execute":"qmp_capabilities"}

* Power on the mouse:
{"execute":"human-monitor-command","arguments":{"command-line":"o 0x2fc 0x03"}}

* Inject a movement event with buttons:
{"execute":"input-send-event","arguments":{"events":[{"type":"rel","data":{"axis":"x","value":30}},{"type":"rel","data":{"axis":"y","value":-10}},{"type":"btn","data":{"button":"left","down":true}},{"type":"btn","data":{"button":"middle","down":true}}]}}

Thread 1 "qemu-system-x86" hit Breakpoint 1, msmouse_input_sync (dev=0x555557648a00)
    at ../chardev/msmouse.c:171
171	    MouseChardev *mouse = MOUSE_CHARDEV(dev);
(gdb) next
172	    Chardev *chr = CHARDEV(dev);
(gdb) next
175	    if (!MSMOUSE_PWR(mouse->tiocm)) {
(gdb) print mouse->tiocm
$17 = 6
(gdb) print mouse->axis[1]
$18 = -10
(gdb) print mouse->axis[0]
$19 = 30
(gdb) print mouse->btns[0]
$20 = true
(gdb)  print mouse->btns[2]
$21 = false
(gdb) print mouse->btnc[0]
$22 = true
(gdb)  print mouse->btnc[2]
$23 = false
(gdb) print mouse->outbuf 
$24 = {data = 0x5555578bbdf0 "M3\b\001$1-5\020\020\020\021<<-/53%<<1%-5", capacity = 64, 
  head = 1, num = 55}
(gdb) continue

* 4. Start migration until completed

{"execute":"migrate","arguments":{"uri":"tcp:127.0.0.1:4444"}}

* 5. Verify mouse data on dest

* As-is:
Thread 1 "qemu-system-x86" hit Breakpoint 1, msmouse_chr_ioctl (chr=0x555557648ad0, 
    cmd=1, arg=0x7ffff415ba80) at ../chardev/msmouse.c:182
182	{
(gdb) next
183	    MouseChardev *mouse = MOUSE_CHARDEV(chr);
(gdb) next
184	    int c, i, j;
(gdb) print ((MouseChardev *)chr)->tiocm
$1 = 0
(gdb) print ((MouseChardev *)chr)->axis[0]
$2 = 0
(gdb) print ((MouseChardev *)chr)->axis[1]
$3 = 0
(gdb) print ((MouseChardev *)chr)->btns[0]
$4 = false
(gdb) print ((MouseChardev *)chr)->btns[2]
$5 = false
(gdb) print ((MouseChardev *)chr)->btnc[0]
$6 = false
(gdb) print ((MouseChardev *)chr)->btnc[2]
$7 = false
(gdb) print ((MouseChardev *)chr)->outbuf
$8 = {data = 0x5555578bbf60 "\273xUU\005", capacity = 64, head = 0, num = 0}
(gdb) 

* To-be:
Thread 1 "qemu-system-x86" hit Breakpoint 1, msmouse_chr_ioctl (chr=0x555557648ad0, 
    cmd=1, arg=0x7ffff415ba80) at ../chardev/msmouse.c:197
197    {
(gdb) next
198        MouseChardev *mouse = MOUSE_CHARDEV(chr);
(gdb) next
199        int c, i, j;
(gdb) print ((MouseChardev *)chr)->tiocm
$1 = 6
(gdb) print ((MouseChardev *)chr)->axis[0]
$2 = 0
(gdb) print ((MouseChardev *)chr)->axis[1]
$3 = 0
(gdb) print ((MouseChardev *)chr)->btns[0]
$4 = true
(gdb) print ((MouseChardev *)chr)->btns[2]
$5 = false
(gdb) print ((MouseChardev *)chr)->btnc[0]
$6 = true
(gdb) print ((MouseChardev *)chr)->btnc[2]
$7 = false
(gdb) print ((MouseChardev *)chr)->outbuf
$8 = {data = 0x5555578bbf60 "M3\b\001$1-5\020\020\020\021<<-/53%<<1%-5", capacity = 64, 
  head = 1, num = 59}

 chardev/msmouse.c | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/chardev/msmouse.c b/chardev/msmouse.c
index 365f04546e..e991135be9 100644
--- a/chardev/msmouse.c
+++ b/chardev/msmouse.c
@@ -30,6 +30,7 @@
 #include "ui/console.h"
 #include "ui/input.h"
 #include "qom/object.h"
+#include "migration/vmstate.h"
 
 #define MSMOUSE_LO6(n)  ((n) & 0x3f)
 #define MSMOUSE_HI2(n)  (((n) & 0xc0) >> 6)
@@ -70,6 +71,20 @@ typedef struct MouseChardev MouseChardev;
 DECLARE_INSTANCE_CHECKER(MouseChardev, MOUSE_CHARDEV,
                          TYPE_CHARDEV_MSMOUSE)
 
+static const VMStateDescription vmstate_msmouse = {
+    .name = "msmouse",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (const VMStateField[]) {
+        VMSTATE_INT32(tiocm, MouseChardev),
+        VMSTATE_INT32_ARRAY(axis, MouseChardev, INPUT_AXIS__MAX),
+        VMSTATE_BOOL_ARRAY(btns, MouseChardev, INPUT_BUTTON__MAX),
+        VMSTATE_BOOL_ARRAY(btnc, MouseChardev, INPUT_BUTTON__MAX),
+        VMSTATE_FIFO8(outbuf, MouseChardev),
+        VMSTATE_END_OF_LIST()
+    },
+};
+
 static void msmouse_chr_accept_input(Chardev *chr)
 {
     MouseChardev *mouse = MOUSE_CHARDEV(chr);
@@ -247,6 +262,7 @@ static void char_msmouse_finalize(Object *obj)
 {
     MouseChardev *mouse = MOUSE_CHARDEV(obj);
 
+    vmstate_unregister(NULL, &vmstate_msmouse, mouse);
     if (mouse->hs) {
         qemu_input_handler_unregister(mouse->hs);
     }
@@ -263,6 +279,7 @@ static bool msmouse_chr_open(Chardev *chr,
                                             &msmouse_handler);
     mouse->tiocm = 0;
     fifo8_create(&mouse->outbuf, MSMOUSE_BUF_SZ);
+    vmstate_register_any(NULL, &vmstate_msmouse, mouse);
 
     /* Never send CHR_EVENT_OPENED */
     return true;
-- 
2.43.0
Re: [PATCH] chardev/msmouse: save and restore device state for migration and savevm
Posted by Peter Maydell 2 days, 21 hours ago
On Sat, 18 Apr 2026 at 12:53, Trieu Huynh <vikingtc4@gmail.com> wrote:
>
> From: Trieu Huynh <vikingtc4@gmail.com>
>
> MouseChardev holds state that must survive migration and savevm/loadvm:
>
>   tiocm   - serial line control (RTS/DTR).  When zero the mouse is
>             considered powered-off and all input events are silently
>             dropped.  Losing this field after migration leaves the
>             mouse in a dead state: the guest driver must toggle
>             RTS/DTR again to re-detect the mouse, which most drivers
>             (Linux sermouse, DOS ctmouse) only do at initialisation.
>
>   axis[]  - accumulated X/Y movement not yet encoded into a packet.
>
>   btns[]  - current button state.
>
>   btnc[]  - per-button change flags used for middle-button tracking.
>
>   outbuf  - Fifo8 of encoded mouse bytes waiting to be delivered to
>             the serial port receive FIFO.
>
> Add a VMStateDescription covering all five fields, registering it in
> msmouse_chr_open and unregistering it in char_msmouse_finalize.  This
> follows the same pattern used by ui/vdagent.c
>
> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1234
>
> Signed-off-by: Trieu Huynh <vikingtc4@gmail.com>

> @@ -263,6 +279,7 @@ static bool msmouse_chr_open(Chardev *chr,
>                                              &msmouse_handler);
>      mouse->tiocm = 0;
>      fifo8_create(&mouse->outbuf, MSMOUSE_BUF_SZ);
> +    vmstate_register_any(NULL, &vmstate_msmouse, mouse);

Adding a new use of vmstate_register_any() is a bit
unfortunate -- this is supposed to be a legacy function.

More generally, it seems odd that the chardev has any state
that needs to be migrated at all. The chardev should have
state corresponding to the host's situation, which is all fresh
for the migration destination. State corresponding to things the
guest has or has not done ought to be in a device model somewhere,
surely?

Put another way, why is this msmouse code in chardev/ and a subtype
of TYPE_CHARDEV, and not a device in hw/input that's a subtype
of TYPE_DEVICE? I guess that the idea is we want to be able to
plug it into any UART device, but that seems to have some issues
when it comes to migration. wctablet is probably in the same boat.

-- PMM