From patchwork Mon May 7 11:42:48 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: 10383977 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 5692560159 for ; Mon, 7 May 2018 12:33:56 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 3692328B09 for ; Mon, 7 May 2018 12:33:56 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 2B68A28B0B; Mon, 7 May 2018 12:33:56 +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 0CD9D28B09 for ; Mon, 7 May 2018 12:33:54 +0000 (UTC) Received: from localhost ([::1]:45958 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fFfKz-0007Lc-GV for patchwork-qemu-devel@patchwork.kernel.org; Mon, 07 May 2018 08:33:53 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:50625) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fFfIP-0006IZ-Lx for qemu-devel@nongnu.org; Mon, 07 May 2018 08:31:15 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1fFfIJ-0004u6-9F for qemu-devel@nongnu.org; Mon, 07 May 2018 08:31:13 -0400 Received: from mail1.hostfission.com ([139.99.139.48]:49820) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fFfII-0004sU-NG for qemu-devel@nongnu.org; Mon, 07 May 2018 08:31:07 -0400 Received: from moya.office.hostfission.com (office.hostfission.com [158.140.217.90]) by mail1.hostfission.com (Postfix) with ESMTP id D54DE41ED8; Mon, 7 May 2018 22:31:05 +1000 (AEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=hostfission.com; s=mail; t=1525696265; bh=2+VeFg2IR3QaFDSlEXJ3nMGsMxov4EGqTckHuqduudg=; h=From:Date:Subject:To:From; b=grTO0XWOBgutxreOVPRljD/N5Ozr9l68I1O1PisUsnUlqVVqJm1u/Kru9DOUmeX73 V+hJ5l2nCzoxKJNUJN2kj7iOWws504759H+xGLmlGDuilTsGh+YnGD7vshYWoKhpqo EzhOT68cx3ETMmvDAou3OlU3sHLkO4FYMMp9cyYs= Received: by moya.office.hostfission.com (Postfix, from userid 0) id C6DF63818FB; Mon, 7 May 2018 22:31:05 +1000 (AEST) Date: Mon, 7 May 2018 21:42:48 +1000 To: qemu-devel@nongnu.org Message-Id: <20180507123105.C6DF63818FB@moya.office.hostfission.com> 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] (resend)[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: Geoffrey McRae via Qemu-devel From: "Denis V. Lunev\" via" Reply-To: Geoffrey McRae 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(-) 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); } } @@ -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; }