From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1LXcdT-0002NC-HM for qemu-devel@nongnu.org; Thu, 12 Feb 2009 09:33:51 -0500 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1LXcdS-0002Kn-7G for qemu-devel@nongnu.org; Thu, 12 Feb 2009 09:33:50 -0500 Received: from [199.232.76.173] (port=41425 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1LXcdR-0002K1-Ub for qemu-devel@nongnu.org; Thu, 12 Feb 2009 09:33:50 -0500 Received: from mx20.gnu.org ([199.232.41.8]:10852) by monty-python.gnu.org with esmtps (TLS-1.0:RSA_AES_256_CBC_SHA1:32) (Exim 4.60) (envelope-from ) id 1LXcdR-0003SU-Ie for qemu-devel@nongnu.org; Thu, 12 Feb 2009 09:33:49 -0500 Received: from mail.codesourcery.com ([65.74.133.4]) by mx20.gnu.org with esmtp (Exim 4.60) (envelope-from ) id 1LXcdQ-0001YZ-6Y for qemu-devel@nongnu.org; Thu, 12 Feb 2009 09:33:48 -0500 From: Vladimir Prus Date: Thu, 12 Feb 2009 17:33:43 +0300 MIME-Version: 1.0 Content-Type: Multipart/Mixed; boundary="Boundary-00=_HNDlJFPXULqIfA2" Message-Id: <200902121733.43146.vladimir@codesourcery.com> Subject: [Qemu-devel] Fix ptimer_get_count overflow. Reply-To: qemu-devel@nongnu.org List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org --Boundary-00=_HNDlJFPXULqIfA2 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Content-Disposition: inline At present, ptimers have a slight inaccuracy that can lead to overflow. The period is stored as 64.32 fixed point number. ptimer_reload uses both integer and fractional part of the perion when computing when the next event should happen. ptimer_get_count, however, uses only integer part of the period. Therefore, when ptimer_get_count is called very soon after ptimer_reload the value returned may be greater than the value ptimer_reload has set. And if the counter was 0xFFFFFFFF, this may result in overflow. This was observed in SH4A emulation as "time jumps", where system time gets magically increased by 10 minutes sometimes. This patch fixes this. The math is not the most accurate possible, but it's relatively fast and fixes the problems. - Volodya --Boundary-00=_HNDlJFPXULqIfA2 Content-Type: text/x-diff; charset="iso 8859-15"; name="primer_overflow.diff" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="primer_overflow.diff" commit f1b78f0ca2b7fe12cdaabcb97c08484d3c26b99f Author: Vladimir Prus Date: Thu Dec 25 17:20:38 2008 +0300 Fix ptimer_get_count overflow. * hw/ptimer.c (ptimer_get_count): Don't ignore fractional part of period. diff --git a/hw/ptimer.c b/hw/ptimer.c index 9d38627..9278f83 100644 --- a/hw/ptimer.c +++ b/hw/ptimer.c @@ -7,7 +7,7 @@ */ #include "hw.h" #include "qemu-timer.h" - +#include "host-utils.h" struct ptimer_state { @@ -78,10 +78,41 @@ uint64_t ptimer_get_count(ptimer_state *s) } else { uint64_t rem; uint64_t div; + uint32_t frac; + int clz1, clz2; + int shift; + + /* We need to divide time by period, where time is stored in + rem (64-bit integer) and period is stored in period/period_frac + (64.32 fixed point). The result should be rounded down, so that + we never get the value greater than the timer's limit. + + We normalize both numerator and demoninator by left-shifting + until one of them has non-zero MSB, and then do 64-bit division. + */ rem = s->next_event - now; div = s->period; - counter = rem / div; + + clz1 = clz64(rem); + clz2 = clz64(div); + shift = clz1 < clz2 ? clz1 : clz2; + + rem <<= shift; + div <<= shift; + if (shift <= 32) + div |= (unsigned long long)( + (s->period_frac >> (32 - shift)) & ((1ull << shift) - 1)); + else + div |= (s->period_frac << (shift - 32)); + + /* Look at remaining bits of period_frac and round div up if + necessary. */ + frac = s->period_frac << shift; + if (frac) + div += 1; + + counter = rem/div; } } else { counter = s->delta; --Boundary-00=_HNDlJFPXULqIfA2--