9ae3a8
From 7d2e8f9662feb64c0b15b6fd53e06e3c56921f27 Mon Sep 17 00:00:00 2001
9ae3a8
From: Paolo Bonzini <pbonzini@redhat.com>
9ae3a8
Date: Fri, 9 Jun 2017 11:43:58 +0200
9ae3a8
Subject: [PATCH 3/6] serial: fixing vmstate for save/restore
9ae3a8
9ae3a8
RH-Author: Paolo Bonzini <pbonzini@redhat.com>
9ae3a8
Message-id: <20170609114359.13036-3-pbonzini@redhat.com>
9ae3a8
Patchwork-id: 75567
9ae3a8
O-Subject: [RHEL7.4 qemu-kvm PATCH v2 2/3] serial: fixing vmstate for save/restore
9ae3a8
Bugzilla: 1452067
9ae3a8
RH-Acked-by: David Hildenbrand <david@redhat.com>
9ae3a8
RH-Acked-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
9ae3a8
RH-Acked-by: Laszlo Ersek <lersek@redhat.com>
9ae3a8
9ae3a8
From: Pavel Dovgalyuk <Pavel.Dovgaluk@ispras.ru>
9ae3a8
9ae3a8
Some fields were added to VMState by this patch to preserve correct
9ae3a8
loading of the serial port controller state.
9ae3a8
Updating FCR value while loading was also modified to disable generating
9ae3a8
an interrupt by loadvm.
9ae3a8
9ae3a8
Signed-off-by: Pavel Dovgalyuk <Pavel.Dovgaluk@ispras.ru>
9ae3a8
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
9ae3a8
(cherry picked from commit 7385b275d9ae8bdf3c012bc4e2ae9779fcea6312)
9ae3a8
9ae3a8
[RHEL: omit some subsections.  thr_ipending can be reconstructed fairly
9ae3a8
       reliably by serial_post_load.  The others are features that are
9ae3a8
       unlikely to be used in RHEL, respectively receive timeout (Linux
9ae3a8
       does not even have the UART_IIR_CTI symbol in the driver) and
9ae3a8
       physical serial ports connected to a modem]
9ae3a8
9ae3a8
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
9ae3a8
---
9ae3a8
 hw/char/serial.c | 245 ++++++++++++++++++++++++++++++++++++++++++++++++-------
9ae3a8
 1 file changed, 215 insertions(+), 30 deletions(-)
9ae3a8
9ae3a8
diff --git a/hw/char/serial.c b/hw/char/serial.c
9ae3a8
index 39de1ca..0518a6f 100644
9ae3a8
--- a/hw/char/serial.c
9ae3a8
+++ b/hw/char/serial.c
9ae3a8
@@ -275,6 +275,36 @@ static void serial_xmit(SerialState *s)
9ae3a8
     s->lsr |= UART_LSR_TEMT;
9ae3a8
 }
9ae3a8
 
9ae3a8
+/* Setter for FCR.
9ae3a8
+   is_load flag means, that value is set while loading VM state
9ae3a8
+   and interrupt should not be invoked */
9ae3a8
+static void serial_write_fcr(SerialState *s, uint8_t val)
9ae3a8
+{
9ae3a8
+    /* Set fcr - val only has the bits that are supposed to "stick" */
9ae3a8
+    s->fcr = val;
9ae3a8
+
9ae3a8
+    if (val & UART_FCR_FE) {
9ae3a8
+        s->iir |= UART_IIR_FE;
9ae3a8
+        /* Set recv_fifo trigger Level */
9ae3a8
+        switch (val & 0xC0) {
9ae3a8
+        case UART_FCR_ITL_1:
9ae3a8
+            s->recv_fifo_itl = 1;
9ae3a8
+            break;
9ae3a8
+        case UART_FCR_ITL_2:
9ae3a8
+            s->recv_fifo_itl = 4;
9ae3a8
+            break;
9ae3a8
+        case UART_FCR_ITL_3:
9ae3a8
+            s->recv_fifo_itl = 8;
9ae3a8
+            break;
9ae3a8
+        case UART_FCR_ITL_4:
9ae3a8
+            s->recv_fifo_itl = 14;
9ae3a8
+            break;
9ae3a8
+        }
9ae3a8
+    } else {
9ae3a8
+        s->iir &= ~UART_IIR_FE;
9ae3a8
+    }
9ae3a8
+}
9ae3a8
+
9ae3a8
 static void serial_ioport_write(void *opaque, hwaddr addr, uint64_t val,
9ae3a8
                                 unsigned size)
9ae3a8
 {
9ae3a8
@@ -351,21 +381,17 @@ static void serial_ioport_write(void *opaque, hwaddr addr, uint64_t val,
9ae3a8
         }
9ae3a8
         break;
9ae3a8
     case 2:
9ae3a8
-        val = val & 0xFF;
9ae3a8
-
9ae3a8
-        if (s->fcr == val)
9ae3a8
-            break;
9ae3a8
-
9ae3a8
         /* Did the enable/disable flag change? If so, make sure FIFOs get flushed */
9ae3a8
-        if ((val ^ s->fcr) & UART_FCR_FE)
9ae3a8
+        if ((val ^ s->fcr) & UART_FCR_FE) {
9ae3a8
             val |= UART_FCR_XFR | UART_FCR_RFR;
9ae3a8
+        }
9ae3a8
 
9ae3a8
         /* FIFO clear */
9ae3a8
 
9ae3a8
         if (val & UART_FCR_RFR) {
9ae3a8
             s->lsr &= ~(UART_LSR_DR | UART_LSR_BI);
9ae3a8
             qemu_del_timer(s->fifo_timeout_timer);
9ae3a8
-            s->timeout_ipending=0;
9ae3a8
+            s->timeout_ipending = 0;
9ae3a8
             fifo8_reset(&s->recv_fifo);
9ae3a8
         }
9ae3a8
 
9ae3a8
@@ -375,28 +401,7 @@ static void serial_ioport_write(void *opaque, hwaddr addr, uint64_t val,
9ae3a8
             fifo8_reset(&s->xmit_fifo);
9ae3a8
         }
9ae3a8
 
9ae3a8
-        if (val & UART_FCR_FE) {
9ae3a8
-            s->iir |= UART_IIR_FE;
9ae3a8
-            /* Set recv_fifo trigger Level */
9ae3a8
-            switch (val & 0xC0) {
9ae3a8
-            case UART_FCR_ITL_1:
9ae3a8
-                s->recv_fifo_itl = 1;
9ae3a8
-                break;
9ae3a8
-            case UART_FCR_ITL_2:
9ae3a8
-                s->recv_fifo_itl = 4;
9ae3a8
-                break;
9ae3a8
-            case UART_FCR_ITL_3:
9ae3a8
-                s->recv_fifo_itl = 8;
9ae3a8
-                break;
9ae3a8
-            case UART_FCR_ITL_4:
9ae3a8
-                s->recv_fifo_itl = 14;
9ae3a8
-                break;
9ae3a8
-            }
9ae3a8
-        } else
9ae3a8
-            s->iir &= ~UART_IIR_FE;
9ae3a8
-
9ae3a8
-        /* Set fcr - or at least the bits in it that are supposed to "stick" */
9ae3a8
-        s->fcr = val & 0xC9;
9ae3a8
+        serial_write_fcr(s, val & 0xC9);
9ae3a8
         serial_update_irq(s);
9ae3a8
         break;
9ae3a8
     case 3:
9ae3a8
@@ -617,6 +622,14 @@ static void serial_pre_save(void *opaque)
9ae3a8
     s->fcr_vmstate = s->fcr;
9ae3a8
 }
9ae3a8
 
9ae3a8
+static int serial_pre_load(void *opaque)
9ae3a8
+{
9ae3a8
+    SerialState *s = opaque;
9ae3a8
+    s->thr_ipending = -1;
9ae3a8
+    s->poll_msl = -1;
9ae3a8
+    return 0;
9ae3a8
+}
9ae3a8
+
9ae3a8
 static int serial_post_load(void *opaque, int version_id)
9ae3a8
 {
9ae3a8
     SerialState *s = opaque;
9ae3a8
@@ -628,17 +641,159 @@ static int serial_post_load(void *opaque, int version_id)
9ae3a8
         s->tsr_retry = MAX_XMIT_RETRY;
9ae3a8
     }
9ae3a8
 
9ae3a8
+    if (s->thr_ipending == -1) {
9ae3a8
+        s->thr_ipending = ((s->iir & UART_IIR_ID) == UART_IIR_THRI);
9ae3a8
+    }
9ae3a8
+    s->last_break_enable = (s->lcr >> 6) & 1;
9ae3a8
     /* Initialize fcr via setter to perform essential side-effects */
9ae3a8
-    serial_ioport_write(s, 0x02, s->fcr_vmstate, 1);
9ae3a8
+    serial_write_fcr(s, s->fcr_vmstate);
9ae3a8
     serial_update_parameters(s);
9ae3a8
     return 0;
9ae3a8
 }
9ae3a8
 
9ae3a8
+static bool serial_thr_ipending_needed(void *opaque)
9ae3a8
+{
9ae3a8
+#if 0
9ae3a8
+    SerialState *s = opaque;
9ae3a8
+    bool expected_value = ((s->iir & UART_IIR_ID) == UART_IIR_THRI);
9ae3a8
+    return s->thr_ipending != expected_value;
9ae3a8
+#else
9ae3a8
+    /* for migration compatibility with RHEL <= 7.3 */
9ae3a8
+    return 0;
9ae3a8
+#endif
9ae3a8
+}
9ae3a8
+
9ae3a8
+const VMStateDescription vmstate_serial_thr_ipending = {
9ae3a8
+    .name = "serial/thr_ipending",
9ae3a8
+    .version_id = 1,
9ae3a8
+    .minimum_version_id = 1,
9ae3a8
+    .fields = (VMStateField[]) {
9ae3a8
+        VMSTATE_INT32(thr_ipending, SerialState),
9ae3a8
+        VMSTATE_END_OF_LIST()
9ae3a8
+    }
9ae3a8
+};
9ae3a8
+
9ae3a8
+static bool serial_tsr_needed(void *opaque)
9ae3a8
+{
9ae3a8
+    SerialState *s = (SerialState *)opaque;
9ae3a8
+    return s->tsr_retry != 0;
9ae3a8
+}
9ae3a8
+
9ae3a8
+const VMStateDescription vmstate_serial_tsr = {
9ae3a8
+    .name = "serial/tsr",
9ae3a8
+    .version_id = 1,
9ae3a8
+    .minimum_version_id = 1,
9ae3a8
+    .fields = (VMStateField[]) {
9ae3a8
+        VMSTATE_UINT32(tsr_retry, SerialState),
9ae3a8
+        VMSTATE_UINT8(thr, SerialState),
9ae3a8
+        VMSTATE_UINT8(tsr, SerialState),
9ae3a8
+        VMSTATE_END_OF_LIST()
9ae3a8
+    }
9ae3a8
+};
9ae3a8
+
9ae3a8
+static bool serial_recv_fifo_needed(void *opaque)
9ae3a8
+{
9ae3a8
+    SerialState *s = (SerialState *)opaque;
9ae3a8
+    return !fifo8_is_empty(&s->recv_fifo);
9ae3a8
+
9ae3a8
+}
9ae3a8
+
9ae3a8
+const VMStateDescription vmstate_serial_recv_fifo = {
9ae3a8
+    .name = "serial/recv_fifo",
9ae3a8
+    .version_id = 1,
9ae3a8
+    .minimum_version_id = 1,
9ae3a8
+    .fields = (VMStateField[]) {
9ae3a8
+        VMSTATE_STRUCT(recv_fifo, SerialState, 1, vmstate_fifo8, Fifo8),
9ae3a8
+        VMSTATE_END_OF_LIST()
9ae3a8
+    }
9ae3a8
+};
9ae3a8
+
9ae3a8
+static bool serial_xmit_fifo_needed(void *opaque)
9ae3a8
+{
9ae3a8
+    SerialState *s = (SerialState *)opaque;
9ae3a8
+    return !fifo8_is_empty(&s->xmit_fifo);
9ae3a8
+}
9ae3a8
+
9ae3a8
+const VMStateDescription vmstate_serial_xmit_fifo = {
9ae3a8
+    .name = "serial/xmit_fifo",
9ae3a8
+    .version_id = 1,
9ae3a8
+    .minimum_version_id = 1,
9ae3a8
+    .fields = (VMStateField[]) {
9ae3a8
+        VMSTATE_STRUCT(xmit_fifo, SerialState, 1, vmstate_fifo8, Fifo8),
9ae3a8
+        VMSTATE_END_OF_LIST()
9ae3a8
+    }
9ae3a8
+};
9ae3a8
+
9ae3a8
+static bool serial_fifo_timeout_timer_needed(void *opaque)
9ae3a8
+{
9ae3a8
+#if 0
9ae3a8
+    SerialState *s = (SerialState *)opaque;
9ae3a8
+    return timer_pending(s->fifo_timeout_timer);
9ae3a8
+#else
9ae3a8
+    /* for migration compatibility with RHEL <= 7.3 */
9ae3a8
+    return 0;
9ae3a8
+#endif
9ae3a8
+}
9ae3a8
+
9ae3a8
+const VMStateDescription vmstate_serial_fifo_timeout_timer = {
9ae3a8
+    .name = "serial/fifo_timeout_timer",
9ae3a8
+    .version_id = 1,
9ae3a8
+    .minimum_version_id = 1,
9ae3a8
+    .fields = (VMStateField[]) {
9ae3a8
+        VMSTATE_TIMER(fifo_timeout_timer, SerialState),
9ae3a8
+        VMSTATE_END_OF_LIST()
9ae3a8
+    }
9ae3a8
+};
9ae3a8
+
9ae3a8
+static bool serial_timeout_ipending_needed(void *opaque)
9ae3a8
+{
9ae3a8
+#if 0
9ae3a8
+    SerialState *s = (SerialState *)opaque;
9ae3a8
+    return s->timeout_ipending != 0;
9ae3a8
+#else
9ae3a8
+    /* for migration compatibility with RHEL <= 7.3 */
9ae3a8
+    return 0;
9ae3a8
+#endif
9ae3a8
+}
9ae3a8
+
9ae3a8
+const VMStateDescription vmstate_serial_timeout_ipending = {
9ae3a8
+    .name = "serial/timeout_ipending",
9ae3a8
+    .version_id = 1,
9ae3a8
+    .minimum_version_id = 1,
9ae3a8
+    .fields = (VMStateField[]) {
9ae3a8
+        VMSTATE_INT32(timeout_ipending, SerialState),
9ae3a8
+        VMSTATE_END_OF_LIST()
9ae3a8
+    }
9ae3a8
+};
9ae3a8
+
9ae3a8
+static bool serial_poll_needed(void *opaque)
9ae3a8
+{
9ae3a8
+#if 0
9ae3a8
+    SerialState *s = (SerialState *)opaque;
9ae3a8
+    return s->poll_msl >= 0;
9ae3a8
+#else
9ae3a8
+    /* for migration compatibility with RHEL <= 7.3 */
9ae3a8
+    return 0;
9ae3a8
+#endif
9ae3a8
+}
9ae3a8
+
9ae3a8
+const VMStateDescription vmstate_serial_poll = {
9ae3a8
+    .name = "serial/poll",
9ae3a8
+    .version_id = 1,
9ae3a8
+    .minimum_version_id = 1,
9ae3a8
+    .fields = (VMStateField[]) {
9ae3a8
+        VMSTATE_INT32(poll_msl, SerialState),
9ae3a8
+        VMSTATE_TIMER(modem_status_poll, SerialState),
9ae3a8
+        VMSTATE_END_OF_LIST()
9ae3a8
+    }
9ae3a8
+};
9ae3a8
+
9ae3a8
 const VMStateDescription vmstate_serial = {
9ae3a8
     .name = "serial",
9ae3a8
     .version_id = 3,
9ae3a8
     .minimum_version_id = 2,
9ae3a8
     .pre_save = serial_pre_save,
9ae3a8
+    .pre_load = serial_pre_load,
9ae3a8
     .post_load = serial_post_load,
9ae3a8
     .fields      = (VMStateField []) {
9ae3a8
         VMSTATE_UINT16_V(divider, SerialState, 2),
9ae3a8
@@ -652,6 +807,32 @@ const VMStateDescription vmstate_serial = {
9ae3a8
         VMSTATE_UINT8(scr, SerialState),
9ae3a8
         VMSTATE_UINT8_V(fcr_vmstate, SerialState, 3),
9ae3a8
         VMSTATE_END_OF_LIST()
9ae3a8
+    },
9ae3a8
+    .subsections = (VMStateSubsection[]) {
9ae3a8
+        {
9ae3a8
+            .vmsd = &vmstate_serial_thr_ipending,
9ae3a8
+            .needed = &serial_thr_ipending_needed,
9ae3a8
+        } , {
9ae3a8
+            .vmsd = &vmstate_serial_tsr,
9ae3a8
+            .needed = &serial_tsr_needed,
9ae3a8
+        } , {
9ae3a8
+            .vmsd = &vmstate_serial_recv_fifo,
9ae3a8
+            .needed = &serial_recv_fifo_needed,
9ae3a8
+        } , {
9ae3a8
+            .vmsd = &vmstate_serial_xmit_fifo,
9ae3a8
+            .needed = &serial_xmit_fifo_needed,
9ae3a8
+        } , {
9ae3a8
+            .vmsd = &vmstate_serial_fifo_timeout_timer,
9ae3a8
+            .needed = &serial_fifo_timeout_timer_needed,
9ae3a8
+        } , {
9ae3a8
+            .vmsd = &vmstate_serial_timeout_ipending,
9ae3a8
+            .needed = &serial_timeout_ipending_needed,
9ae3a8
+        } , {
9ae3a8
+            .vmsd = &vmstate_serial_poll,
9ae3a8
+            .needed = &serial_poll_needed,
9ae3a8
+        } , {
9ae3a8
+            /* empty */
9ae3a8
+        }
9ae3a8
     }
9ae3a8
 };
9ae3a8
 
9ae3a8
@@ -678,6 +859,10 @@ static void serial_reset(void *opaque)
9ae3a8
     s->char_transmit_time = (get_ticks_per_sec() / 9600) * 10;
9ae3a8
     s->poll_msl = 0;
9ae3a8
 
9ae3a8
+    s->timeout_ipending = 0;
9ae3a8
+    qemu_del_timer(s->fifo_timeout_timer);
9ae3a8
+    qemu_del_timer(s->modem_status_poll);
9ae3a8
+
9ae3a8
     fifo8_reset(&s->recv_fifo);
9ae3a8
     fifo8_reset(&s->xmit_fifo);
9ae3a8
 
9ae3a8
-- 
9ae3a8
1.8.3.1
9ae3a8