Commit 0846740652 for qemu.org

commit 0846740652d1333369265fa075708b5a386c5ff6
Author: Christian Quante <christian@quante.one>
Date:   Tue Aug 25 09:51:26 2026 +0200

    hw/input/ps2: answer unknown mouse commands with a resend

    ps2_write_mouse() ends its command switch with a bare "default: break;",
    so an unknown command draws no reply at all. A real PS/2 device answers
    every byte it is given -- ACK (0xFA) when it understood one, resend
    (0xFE) when it did not -- and a guest that gets nothing back is left
    waiting out its reply timeout. The keyboard path in the same file has
    answered unknown commands with KBD_REPLY_RESEND since commit
    06b3611fc2a3 ("ps2: reject unknown commands, instead of blindly
    accepting them").

    Two guests were measured on this.

    OS/2 probes the mouse with the vendor command 0xBB, which QEMU does not
    implement, and then polls the status port until its own timeout runs
    out. On a Warp 3 guest that wait costs about 25 ms of every boot under
    TCG, and 2.1 s under KVM, where each of those polls leaves the guest.
    With this patch the wait ends on the first read: the guest takes the
    same error path an unexpected reply would, and does not retry.

    Linux runs into two of them while probing the mouse: the ALPS probe
    sends 0xEC (reset wrap mode), which ps2_write_mouse() only answers
    while the mouse is in wrap mode, and the TrackPoint probe sends 0xE1.
    Each costs libps2 a 200 ms reply timeout. Timing the psmouse detection
    from a mark written to /dev/kmsg to the kernel's "input:" line, three
    boots each of a 6.18.35 kernel under TCG: 426.7/428.8/441.6 ms without
    this patch, 21.4/21.6/21.2 ms with it. The mouse is detected
    identically either way; only the error the probe ends in changes, from
    -EIO (nothing came back at all) to -EPROTO (libps2 gives up after its
    second attempt).

    The specification's second stage -- 0xFC (Error) when the byte after a
    rejected one is invalid as well -- is deliberately left out. It would
    need state that has to survive migration, no guest is known to test for
    it, and the keyboard path does without it as well.

    Cc: qemu-stable@nongnu.org
    Signed-off-by: Christian Quante <christian@quante.one>
    Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
    Reviewed-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
    Message-ID: <20260825075127.34876-2-christian@quante.one>

diff --git a/hw/input/ps2.c b/hw/input/ps2.c
index 5516eb262d..e8300d2d83 100644
--- a/hw/input/ps2.c
+++ b/hw/input/ps2.c
@@ -73,6 +73,7 @@
 #define AUX_SET_DEFAULT     0xF6
 #define AUX_RESET           0xFF    /* Reset aux device */
 #define AUX_ACK             0xFA    /* Command byte ACK. */
+#define AUX_RESEND          0xFE    /* Command NACK, send the cmd again */

 #define MOUSE_STATUS_REMOTE     0x40
 #define MOUSE_STATUS_ENABLED    0x20
@@ -955,6 +956,11 @@ void ps2_write_mouse(PS2MouseState *s, int val)
                 s->mouse_type);
             break;
         default:
+            /*
+             * A PS/2 device answers every command it is given; an unknown
+             * one draws a resend.
+             */
+            ps2_queue(ps2, AUX_RESEND);
             break;
         }
         break;