9ae3a8
From 0c6d2ffcebff88c6cda738aa46fa77c09b93b78b Mon Sep 17 00:00:00 2001
9ae3a8
From: Fam Zheng <famz@redhat.com>
9ae3a8
Date: Thu, 18 May 2017 09:21:27 +0200
9ae3a8
Subject: [PATCH 14/18] serial: only resample THR interrupt on rising edge of
9ae3a8
 IER.THRI
9ae3a8
9ae3a8
RH-Author: Fam Zheng <famz@redhat.com>
9ae3a8
Message-id: <20170518092131.16571-15-famz@redhat.com>
9ae3a8
Patchwork-id: 75304
9ae3a8
O-Subject: [RHEL-7.4 qemu-kvm PATCH v3 14/18] serial: only resample THR interrupt on rising edge of IER.THRI
9ae3a8
Bugzilla: 1451470
9ae3a8
RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com>
9ae3a8
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
9ae3a8
RH-Acked-by: Eduardo Habkost <ehabkost@redhat.com>
9ae3a8
9ae3a8
From: Paolo Bonzini <pbonzini@redhat.com>
9ae3a8
9ae3a8
There is disagreement on whether LSR.THRE should be resampled when
9ae3a8
IER.THRI goes from 1 to 1.  Bochs only does it if IER.THRI goes from 0
9ae3a8
to 1; PCE does it even if IER.THRI is unchanged.  But the Windows driver
9ae3a8
seems to always go from 1 to 0 and back to 1, so do things in agreement
9ae3a8
with Bochs, because the handling of thr_ipending was reported in 2010
9ae3a8
(https://lists.gnu.org/archive/html/qemu-devel/2010-03/msg01914.html)
9ae3a8
as breaking DR-DOS Plus.
9ae3a8
9ae3a8
Reported-by: Roy Tam <roytam@gmail.com>
9ae3a8
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
9ae3a8
(cherry picked from commit 1645b8eee558ffe2389a081bf61d08a864c36d2c)
9ae3a8
Signed-off-by: Fam Zheng <famz@redhat.com>
9ae3a8
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
9ae3a8
---
9ae3a8
 hw/char/serial.c | 25 ++++++++++++++++---------
9ae3a8
 1 file changed, 16 insertions(+), 9 deletions(-)
9ae3a8
9ae3a8
diff --git a/hw/char/serial.c b/hw/char/serial.c
9ae3a8
index e0d29a8..9986adf 100644
9ae3a8
--- a/hw/char/serial.c
9ae3a8
+++ b/hw/char/serial.c
9ae3a8
@@ -306,10 +306,12 @@ static void serial_ioport_write(void *opaque, hwaddr addr, uint64_t val,
9ae3a8
             s->divider = (s->divider & 0x00ff) | (val << 8);
9ae3a8
             serial_update_parameters(s);
9ae3a8
         } else {
9ae3a8
+            uint8_t changed = (s->ier ^ val) & 0x0f;
9ae3a8
             s->ier = val & 0x0f;
9ae3a8
             /* If the backend device is a real serial port, turn polling of the modem
9ae3a8
-               status lines on physical port on or off depending on UART_IER_MSI state */
9ae3a8
-            if (s->poll_msl >= 0) {
9ae3a8
+             * status lines on physical port on or off depending on UART_IER_MSI state.
9ae3a8
+             */
9ae3a8
+            if ((changed & UART_IER_MSI) && s->poll_msl >= 0) {
9ae3a8
                 if (s->ier & UART_IER_MSI) {
9ae3a8
                      s->poll_msl = 1;
9ae3a8
                      serial_update_msl(s);
9ae3a8
@@ -324,18 +326,23 @@ static void serial_ioport_write(void *opaque, hwaddr addr, uint64_t val,
9ae3a8
              * This is not in the datasheet, but Windows relies on it.  It is
9ae3a8
              * unclear if THRE has to be resampled every time THRI becomes
9ae3a8
              * 1, or only on the rising edge.  Bochs does the latter, and Windows
9ae3a8
-             * always toggles IER to all zeroes and back to all ones.  But for
9ae3a8
-             * now leave it as it has always been in QEMU.
9ae3a8
+             * always toggles IER to all zeroes and back to all ones, so do the
9ae3a8
+             * same.
9ae3a8
              *
9ae3a8
              * If IER.THRI is zero, thr_ipending is not used.  Set it to zero
9ae3a8
              * so that the thr_ipending subsection is not migrated.
9ae3a8
              */
9ae3a8
-            if ((s->ier & UART_IER_THRI) && (s->lsr & UART_LSR_THRE)) {
9ae3a8
-                s->thr_ipending = 1;
9ae3a8
-            } else {
9ae3a8
-                s->thr_ipending = 0;
9ae3a8
+            if (changed & UART_IER_THRI) {
9ae3a8
+                if ((s->ier & UART_IER_THRI) && (s->lsr & UART_LSR_THRE)) {
9ae3a8
+                    s->thr_ipending = 1;
9ae3a8
+                } else {
9ae3a8
+                    s->thr_ipending = 0;
9ae3a8
+                }
9ae3a8
+            }
9ae3a8
+
9ae3a8
+            if (changed) {
9ae3a8
+                serial_update_irq(s);
9ae3a8
             }
9ae3a8
-            serial_update_irq(s);
9ae3a8
         }
9ae3a8
         break;
9ae3a8
     case 2:
9ae3a8
-- 
9ae3a8
1.8.3.1
9ae3a8