diff mbox

[V3] rtc: fix a infinite loop in windows vm startup

Message ID 1500921322-36875-1-git-send-email-peng.hao2@zte.com.cn (mailing list archive)
State New, archived
Headers show

Commit Message

Peng Hao July 24, 2017, 6:35 p.m. UTC
When a windows vm starts, periodic timer of rtc will stop several times.
windows kernel will check whether REG_A_UIP is changed. REG_C's interrupt
flags will not be cleared when periodic timer stops and the update timer
will switch to alarm timer. So the expiration time of alarm timer is very
long and REG_A_UIP will not vary.At last windows kernel will repeat to 
check REG_A_UIP all the time.

Signed-off-by: Peng Hao <peng.hao2@zte.com.cn>
Signed-off-by: Liu Yi <liu.yi24@zte.com.cn>
---
 hw/timer/mc146818rtc.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

Comments

Paolo Bonzini July 24, 2017, 11:53 a.m. UTC | #1
On 24/07/2017 20:35, Peng Hao wrote:
> When a windows vm starts, periodic timer of rtc will stop several times.
> windows kernel will check whether REG_A_UIP is changed. REG_C's interrupt
> flags will not be cleared when periodic timer stops and the update timer
> will switch to alarm timer. So the expiration time of alarm timer is very
> long and REG_A_UIP will not vary.At last windows kernel will repeat to 
> check REG_A_UIP all the time.

This should not happen.  REG_A_UIP is set and cleared in register A
every second, like this:

        case RTC_REG_A:
            if (update_in_progress(s)) {
                s->cmos_data[s->cmos_index] |= REG_A_UIP;
            } else {
                s->cmos_data[s->cmos_index] &= ~REG_A_UIP;
            }
            ret = s->cmos_data[s->cmos_index];
            break;

where update_in_progress does:

    guest_nsec = get_guest_rtc_ns(s);
    /* UIP bit will be set at last 244us of every second. */
    if ((guest_nsec % NANOSECONDS_PER_SECOND) >=
        (NANOSECONDS_PER_SECOND - UIP_HOLD_LENGTH)) {
        return 1;
    }
    return 0;

This is done even if the timer is not pending.

How can the bug be reproduced?

Paolo

> Signed-off-by: Peng Hao <peng.hao2@zte.com.cn>
> Signed-off-by: Liu Yi <liu.yi24@zte.com.cn>
> ---
>  hw/timer/mc146818rtc.c | 4 +++-
>  1 file changed, 3 insertions(+), 1 deletion(-)
> 
> diff --git a/hw/timer/mc146818rtc.c b/hw/timer/mc146818rtc.c
> index 1b8d3d7..aa55fae 100644
> --- a/hw/timer/mc146818rtc.c
> +++ b/hw/timer/mc146818rtc.c
> @@ -457,6 +457,8 @@ static void rtc_update_timer(void *opaque)
>      if ((new_irqs & s->cmos_data[RTC_REG_B]) != 0) {
>          s->cmos_data[RTC_REG_C] |= REG_C_IRQF;
>          qemu_irq_raise(s->irq);
> +    } else if ((s->cmos_data[RTC_REG_B] & REG_B_UIE) == 0) {
> +        s->cmos_data[RTC_REG_C] &= ~REG_C_UF;
>      }
>      check_update_timer(s);
>  }
> @@ -559,7 +561,7 @@ static void cmos_ioport_write(void *opaque, hwaddr addr,
>                  s->cmos_data[RTC_REG_C] |= REG_C_IRQF;
>                  qemu_irq_raise(s->irq);
>              } else {
> -                s->cmos_data[RTC_REG_C] &= ~REG_C_IRQF;
> +                s->cmos_data[RTC_REG_C] &= ~(REG_C_UF | REG_C_IRQF);
>                  qemu_irq_lower(s->irq);
>              }
>              s->cmos_data[RTC_REG_B] = data;
>
Eric Blake July 24, 2017, 11:54 a.m. UTC | #2
On 07/24/2017 01:35 PM, Peng Hao wrote:
> When a windows vm starts, periodic timer of rtc will stop several times.
> windows kernel will check whether REG_A_UIP is changed. REG_C's interrupt
> flags will not be cleared when periodic timer stops and the update timer
> will switch to alarm timer. So the expiration time of alarm timer is very
> long and REG_A_UIP will not vary.At last windows kernel will repeat to 
> check REG_A_UIP all the time.
> 
> Signed-off-by: Peng Hao <peng.hao2@zte.com.cn>
> Signed-off-by: Liu Yi <liu.yi24@zte.com.cn>
> ---

When posting a v3, it's useful to tell us (after the --- separator) what
changed from v2, to help us focus on why a v3 was needed.
diff mbox

Patch

diff --git a/hw/timer/mc146818rtc.c b/hw/timer/mc146818rtc.c
index 1b8d3d7..aa55fae 100644
--- a/hw/timer/mc146818rtc.c
+++ b/hw/timer/mc146818rtc.c
@@ -457,6 +457,8 @@  static void rtc_update_timer(void *opaque)
     if ((new_irqs & s->cmos_data[RTC_REG_B]) != 0) {
         s->cmos_data[RTC_REG_C] |= REG_C_IRQF;
         qemu_irq_raise(s->irq);
+    } else if ((s->cmos_data[RTC_REG_B] & REG_B_UIE) == 0) {
+        s->cmos_data[RTC_REG_C] &= ~REG_C_UF;
     }
     check_update_timer(s);
 }
@@ -559,7 +561,7 @@  static void cmos_ioport_write(void *opaque, hwaddr addr,
                 s->cmos_data[RTC_REG_C] |= REG_C_IRQF;
                 qemu_irq_raise(s->irq);
             } else {
-                s->cmos_data[RTC_REG_C] &= ~REG_C_IRQF;
+                s->cmos_data[RTC_REG_C] &= ~(REG_C_UF | REG_C_IRQF);
                 qemu_irq_lower(s->irq);
             }
             s->cmos_data[RTC_REG_B] = data;