From patchwork Mon May 7 12:03:10 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Denis V. Lunev\" via" X-Patchwork-Id: 10383943 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 3A6BF6037F for ; Mon, 7 May 2018 12:04:20 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 393C328AF7 for ; Mon, 7 May 2018 12:04:20 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 2ACDC28AFA; Mon, 7 May 2018 12:04:20 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.8 required=2.0 tests=BAYES_00,DKIM_SIGNED, MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI, T_DKIM_INVALID autolearn=ham version=3.3.1 Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 48BC428AF7 for ; Mon, 7 May 2018 12:04:19 +0000 (UTC) Received: from localhost ([::1]:45860 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fFesM-000819-I5 for patchwork-qemu-devel@patchwork.kernel.org; Mon, 07 May 2018 08:04:18 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:44921) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fFerN-0007Nv-HB for qemu-devel@nongnu.org; Mon, 07 May 2018 08:03:19 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1fFerI-0000UB-Ir for qemu-devel@nongnu.org; Mon, 07 May 2018 08:03:17 -0400 Received: from mail1.hostfission.com ([139.99.139.48]:49522) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fFerI-0000Te-0X for qemu-devel@nongnu.org; Mon, 07 May 2018 08:03:12 -0400 Received: from www1.hostfission.com (www1.hostfission.com [139.99.139.52]) by mail1.hostfission.com (Postfix) with ESMTP id A669C43ED6 for ; Mon, 7 May 2018 22:03:10 +1000 (AEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=hostfission.com; s=mail; t=1525694590; bh=NN+n5x03fNcwmOq8f9J+doxmqwgIWmAgLUfjz7aHzXo=; h=To:Subject:Date:From:From; b=cacK8BBkvnMBMi85ALfHovfxQ5EmUqjuUMzVRSUQcYyR1pgSD9hVW2Prz85pAF9iT e3ox9/nNC5hFRtL7OwrBgvN8azZpWU4Jq4njlf4D0cu4Q1lrxac3Hc4ugOD4LoYv3J N+HBNYWHPf7/CM1KImdOmkfIk0rAgXZOzF2dsKvM= Received: by www1.hostfission.com (Postfix, from userid 1000) id 9D40181115; Mon, 7 May 2018 22:03:10 +1000 (AEST) To: qemu-devel@nongnu.org X-PHP-Originating-Script: 0:rcube.php MIME-Version: 1.0 Date: Mon, 07 May 2018 22:03:10 +1000 Message-ID: X-Sender: geoff@hostfission.com User-Agent: Roundcube Webmail/1.2.3 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 139.99.139.48 Subject: [Qemu-devel] [PATCH 2/2] ps2: Fix mouse stream corruption due to lost data X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: geoff--- via Qemu-devel From: "Denis V. Lunev\" via" Reply-To: geoff@hostfission.com Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP This fixes an issue by adding bounds checking to multi-byte packets where the PS/2 mouse data stream may become corrupted due to data being discarded when the PS/2 ringbuffer is full. Interrupts for Multi-byte responses are postponed until the final byte has been queued. These changes fix a bug where windows guests drop the mouse device entirely requring the guest to be restarted. Signed-off-by: Geoffrey McRae --- hw/input/pckbd.c | 6 +-- hw/input/ps2.c | 160 +++++++++++++++++++++++++++++++++++++------------------ 2 files changed, 110 insertions(+), 56 deletions(-) } @@ -490,72 +528,80 @@ void ps2_write_keyboard(void *opaque, int val) case -1: switch(val) { case 0x00: - ps2_queue(&s->common, KBD_REPLY_ACK); + ps2_queue_raise(&s->common, KBD_REPLY_ACK); break; case 0x05: - ps2_queue(&s->common, KBD_REPLY_RESEND); + ps2_queue_raise(&s->common, KBD_REPLY_RESEND); break; case KBD_CMD_GET_ID: - ps2_queue(&s->common, KBD_REPLY_ACK); /* We emulate a MF2 AT keyboard here */ - ps2_queue(&s->common, KBD_REPLY_ID); if (s->translate) - ps2_queue(&s->common, 0x41); + ps2_queue_bytes(&s->common, 3, + KBD_REPLY_ACK, + KBD_REPLY_ID, + 0x41); else - ps2_queue(&s->common, 0x83); + ps2_queue_bytes(&s->common, 3, + KBD_REPLY_ACK, + KBD_REPLY_ID, + 0x83); break; case KBD_CMD_ECHO: - ps2_queue(&s->common, KBD_CMD_ECHO); + ps2_queue_raise(&s->common, KBD_CMD_ECHO); break; case KBD_CMD_ENABLE: s->scan_enabled = 1; - ps2_queue(&s->common, KBD_REPLY_ACK); + ps2_queue_raise(&s->common, KBD_REPLY_ACK); break; case KBD_CMD_SCANCODE: case KBD_CMD_SET_LEDS: case KBD_CMD_SET_RATE: s->common.write_cmd = val; - ps2_queue(&s->common, KBD_REPLY_ACK); + ps2_queue_raise(&s->common, KBD_REPLY_ACK); break; case KBD_CMD_RESET_DISABLE: ps2_reset_keyboard(s); s->scan_enabled = 0; - ps2_queue(&s->common, KBD_REPLY_ACK); + ps2_queue_raise(&s->common, KBD_REPLY_ACK); break; case KBD_CMD_RESET_ENABLE: ps2_reset_keyboard(s); s->scan_enabled = 1; - ps2_queue(&s->common, KBD_REPLY_ACK); + ps2_queue_raise(&s->common, KBD_REPLY_ACK); break; case KBD_CMD_RESET: ps2_reset_keyboard(s); - ps2_queue(&s->common, KBD_REPLY_ACK); - ps2_queue(&s->common, KBD_REPLY_POR); + ps2_queue_bytes(&s->common, 2, + KBD_REPLY_ACK, + KBD_REPLY_POR); break; default: - ps2_queue(&s->common, KBD_REPLY_RESEND); + ps2_queue_raise(&s->common, KBD_REPLY_RESEND); break; } break; case KBD_CMD_SCANCODE: if (val == 0) { - ps2_queue(&s->common, KBD_REPLY_ACK); - ps2_put_keycode(s, s->scancode_set); + if (s->common.queue.count <= PS2_QUEUE_SIZE - 2) + { + ps2_queue_raise(&s->common, KBD_REPLY_ACK); + ps2_put_keycode(s, s->scancode_set); + } } else if (val >= 1 && val <= 3) { s->scancode_set = val; - ps2_queue(&s->common, KBD_REPLY_ACK); + ps2_queue_raise(&s->common, KBD_REPLY_ACK); } else { - ps2_queue(&s->common, KBD_REPLY_RESEND); + ps2_queue_raise(&s->common, KBD_REPLY_RESEND); } s->common.write_cmd = -1; break; case KBD_CMD_SET_LEDS: ps2_set_ledstate(s, val); - ps2_queue(&s->common, KBD_REPLY_ACK); + ps2_queue_raise(&s->common, KBD_REPLY_ACK); s->common.write_cmd = -1; break; case KBD_CMD_SET_RATE: - ps2_queue(&s->common, KBD_REPLY_ACK); + ps2_queue_raise(&s->common, KBD_REPLY_ACK); s->common.write_cmd = -1; break; } @@ -572,11 +618,15 @@ void ps2_keyboard_set_translation(void *opaque, int mode) s->translate = mode; } -static void ps2_mouse_send_packet(PS2MouseState *s) +static int ps2_mouse_send_packet(PS2MouseState *s) { unsigned int b; int dx1, dy1, dz1; + const int needed = 3 + (s->mouse_type - 2); + if (PS2_QUEUE_SIZE - s->common.queue.count < needed) + return 0; + dx1 = s->mouse_dx; dy1 = s->mouse_dy; dz1 = s->mouse_dz; @@ -614,11 +664,15 @@ static void ps2_mouse_send_packet(PS2MouseState *s) break; } + ps2_raise(&s->common); + trace_ps2_mouse_send_packet(s, dx1, dy1, dz1, b); /* update deltas */ s->mouse_dx -= dx1; s->mouse_dy -= dy1; s->mouse_dz -= dz1; + + return 1; } static void ps2_mouse_event(DeviceState *dev, QemuConsole *src, @@ -680,10 +734,7 @@ static void ps2_mouse_sync(DeviceState *dev) qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER); } if (!(s->mouse_status & MOUSE_STATUS_REMOTE)) { - while (s->common.queue.count < PS2_QUEUE_SIZE - 4) { - /* if not remote, send event. Multiple events are sent if - too big deltas */ - ps2_mouse_send_packet(s); + while(ps2_mouse_send_packet(s)) { if (s->mouse_dx == 0 && s->mouse_dy == 0 && s->mouse_dz == 0) break; } @@ -713,66 +764,68 @@ void ps2_write_mouse(void *opaque, int val) if (s->mouse_wrap) { if (val == AUX_RESET_WRAP) { s->mouse_wrap = 0; - ps2_queue(&s->common, AUX_ACK); + ps2_queue_raise(&s->common, AUX_ACK); return; } else if (val != AUX_RESET) { - ps2_queue(&s->common, val); + ps2_queue_raise(&s->common, val); return; } } switch(val) { case AUX_SET_SCALE11: s->mouse_status &= ~MOUSE_STATUS_SCALE21; - ps2_queue(&s->common, AUX_ACK); + ps2_queue_raise(&s->common, AUX_ACK); break; case AUX_SET_SCALE21: s->mouse_status |= MOUSE_STATUS_SCALE21; - ps2_queue(&s->common, AUX_ACK); + ps2_queue_raise(&s->common, AUX_ACK); break; case AUX_SET_STREAM: s->mouse_status &= ~MOUSE_STATUS_REMOTE; - ps2_queue(&s->common, AUX_ACK); + ps2_queue_raise(&s->common, AUX_ACK); break; case AUX_SET_WRAP: s->mouse_wrap = 1; - ps2_queue(&s->common, AUX_ACK); + ps2_queue_raise(&s->common, AUX_ACK); break; case AUX_SET_REMOTE: s->mouse_status |= MOUSE_STATUS_REMOTE; - ps2_queue(&s->common, AUX_ACK); + ps2_queue_raise(&s->common, AUX_ACK); break; case AUX_GET_TYPE: - ps2_queue(&s->common, AUX_ACK); - ps2_queue(&s->common, s->mouse_type); + ps2_queue_bytes(&s->common, 2, + AUX_ACK, + s->mouse_type); break; case AUX_SET_RES: case AUX_SET_SAMPLE: s->common.write_cmd = val; - ps2_queue(&s->common, AUX_ACK); + ps2_queue_raise(&s->common, AUX_ACK); break; case AUX_GET_SCALE: - ps2_queue(&s->common, AUX_ACK); - ps2_queue(&s->common, s->mouse_status); - ps2_queue(&s->common, s->mouse_resolution); - ps2_queue(&s->common, s->mouse_sample_rate); + ps2_queue_bytes(&s->common, 4, + AUX_ACK, + s->mouse_status, + s->mouse_resolution, + s->mouse_sample_rate); break; case AUX_POLL: - ps2_queue(&s->common, AUX_ACK); + ps2_queue_raise(&s->common, AUX_ACK); ps2_mouse_send_packet(s); break; case AUX_ENABLE_DEV: s->mouse_status |= MOUSE_STATUS_ENABLED; - ps2_queue(&s->common, AUX_ACK); + ps2_queue_raise(&s->common, AUX_ACK); break; case AUX_DISABLE_DEV: s->mouse_status &= ~MOUSE_STATUS_ENABLED; - ps2_queue(&s->common, AUX_ACK); + ps2_queue_raise(&s->common, AUX_ACK); break; case AUX_SET_DEFAULT: s->mouse_sample_rate = 100; s->mouse_resolution = 2; s->mouse_status = 0; - ps2_queue(&s->common, AUX_ACK); + ps2_queue_raise(&s->common, AUX_ACK); break; case AUX_RESET: s->mouse_sample_rate = 100; @@ -780,9 +833,10 @@ void ps2_write_mouse(void *opaque, int val) s->mouse_status = 0; s->mouse_type = 0; ps2_reset_queue(&s->common); - ps2_queue(&s->common, AUX_ACK); - ps2_queue(&s->common, 0xaa); - ps2_queue(&s->common, s->mouse_type); + ps2_queue_bytes(&s->common, 3, + AUX_ACK, + 0xaa, + s->mouse_type); break; default: break; @@ -816,12 +870,12 @@ void ps2_write_mouse(void *opaque, int val) s->mouse_detect_state = 0; break; } - ps2_queue(&s->common, AUX_ACK); + ps2_queue_raise(&s->common, AUX_ACK); s->common.write_cmd = -1; break; case AUX_SET_RES: s->mouse_resolution = val; - ps2_queue(&s->common, AUX_ACK); + ps2_queue_raise(&s->common, AUX_ACK); s->common.write_cmd = -1; break; } diff --git a/hw/input/pckbd.c b/hw/input/pckbd.c index f17f18e51b..004ea3466d 100644 --- a/hw/input/pckbd.c +++ b/hw/input/pckbd.c @@ -216,9 +216,9 @@ static uint64_t kbd_read_status(void *opaque, hwaddr addr, static void kbd_queue(KBDState *s, int b, int aux) { if (aux) - ps2_queue(s->mouse, b); + ps2_queue_raise(s->mouse, b); else - ps2_queue(s->kbd, b); + ps2_queue_raise(s->kbd, b); } static void outport_write(KBDState *s, uint32_t val) diff --git a/hw/input/ps2.c b/hw/input/ps2.c index 6edf046820..011290920f 100644 --- a/hw/input/ps2.c +++ b/hw/input/ps2.c @@ -192,12 +192,50 @@ void ps2_queue(PS2State *s, int b) { PS2Queue *q = &s->queue; - if (q->count >= PS2_QUEUE_SIZE - 1) + if (q->count == PS2_QUEUE_SIZE) + { + printf("Warning! PS2 Queue Overflow!\n"); return; + } + q->data[q->wptr] = b; if (++q->wptr == PS2_QUEUE_SIZE) q->wptr = 0; q->count++; +} + +void ps2_raise(PS2State *s) +{ + s->update_irq(s->update_arg, 1); +} + +void ps2_queue_raise(PS2State *s, int b) +{ + ps2_queue(s, b); + s->update_irq(s->update_arg, 1); +} + +void ps2_queue_bytes(PS2State *s, const int length, ...) +{ + PS2Queue *q = &s->queue; + + if (PS2_QUEUE_SIZE - q->count < length) { + printf("Unable to send %d bytes, buffer full\n", length); + return; + } + + va_list args; + va_start(args, length); + + for(int i = 0; i < length; ++i) + { + q->data[q->wptr] = va_arg(args, int); + if (++q->wptr == PS2_QUEUE_SIZE) + q->wptr = 0; + q->count++; + } + + va_end(args); s->update_irq(s->update_arg, 1); } @@ -213,13 +251,13 @@ static void ps2_put_keycode(void *opaque, int keycode) if (keycode == 0xf0) { s->need_high_bit = true; } else if (s->need_high_bit) { - ps2_queue(&s->common, translate_table[keycode] | 0x80); + ps2_queue_raise(&s->common, translate_table[keycode] | 0x80); s->need_high_bit = false; } else { - ps2_queue(&s->common, translate_table[keycode]); + ps2_queue_raise(&s->common, translate_table[keycode]); } } else { - ps2_queue(&s->common, keycode); + ps2_queue_raise(&s->common, keycode); }