Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/dev/ic It is possible to overflow the (low 32bit) HPET c...



details:   https://anonhg.NetBSD.org/src/rev/294037f35b95
branches:  trunk
changeset: 369471:294037f35b95
user:      mlelstv <mlelstv%NetBSD.org@localhost>
date:      Sat Aug 20 06:47:28 2022 +0000

description:
It is possible to overflow the (low 32bit) HPET counter between hpet_attach
and TSC calibration if the boot is delayed for more than ~430 seconds (or
less, depending on HPET frequency). The result is a badly misconfigured
timecounter.

Change the measurement interval to ~1e6 HPET ticks (<100ms) during
the calibration to avoid the overflow. This introduces an error of
1ppm, compared to the previous unspecified but typical error of 0.1ppm.
But this is still much less than the guaranteed maximum frequency drift
of the HPET counter itself, which is 500ppm.

diffstat:

 sys/dev/ic/hpet.c |  50 ++++++++++++++++++++++++++------------------------
 1 files changed, 26 insertions(+), 24 deletions(-)

diffs (100 lines):

diff -r 55b61e2dc61e -r 294037f35b95 sys/dev/ic/hpet.c
--- a/sys/dev/ic/hpet.c Sat Aug 20 01:02:44 2022 +0000
+++ b/sys/dev/ic/hpet.c Sat Aug 20 06:47:28 2022 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: hpet.c,v 1.17 2020/05/16 23:06:40 ad Exp $ */
+/* $NetBSD: hpet.c,v 1.18 2022/08/20 06:47:28 mlelstv Exp $ */
 
 /*
  * Copyright (c) 2006 Nicolas Joly
@@ -33,7 +33,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: hpet.c,v 1.17 2020/05/16 23:06:40 ad Exp $");
+__KERNEL_RCSID(0, "$NetBSD: hpet.c,v 1.18 2022/08/20 06:47:28 mlelstv Exp $");
 
 #include <sys/systm.h>
 #include <sys/device.h>
@@ -54,8 +54,6 @@
 static bool    hpet_resume(device_t, const pmf_qual_t *);
 
 static struct hpet_softc *hpet0 __read_mostly;
-static uint32_t hpet_attach_val;
-static uint64_t hpet_attach_tsc;
 
 int
 hpet_detach(device_t dv, int flags)
@@ -147,14 +145,6 @@
        eval = bus_space_read_4(sc->sc_memt, sc->sc_memh, HPET_MCOUNT_LO);
        val = eval - sval;
        sc->sc_adj = (int64_t)val * sc->sc_period / 1000;
-
-       /* Store attach-time values for computing TSC frequency later. */
-       if (cpu_hascounter() && sc == hpet0) {
-               (void)cpu_counter();
-               val = bus_space_read_4(sc->sc_memt, sc->sc_memh, HPET_MCOUNT_LO);
-               hpet_attach_tsc = cpu_counter();
-               hpet_attach_val = val;
-       }
 }
 
 static u_int
@@ -214,33 +204,45 @@
 hpet_tsc_freq(void)
 {
        struct hpet_softc *sc;
-       uint64_t td, val, freq;
-       uint32_t hd;
+       uint64_t td0, td, val, freq;
+       uint32_t hd0, hd;
        int s;
 
        if (hpet0 == NULL || !cpu_hascounter())
                return 0;
 
-       /* Slow down if we got here from attach in under 0.1s. */
        sc = hpet0;
-       hd = bus_space_read_4(sc->sc_memt, sc->sc_memh, HPET_MCOUNT_LO);
-       hd -= hpet_attach_val;
-       if (hd < (uint64_t)100000 * 1000000000 / sc->sc_period)
-               hpet_delay(100000);
+
+       s = splhigh();
+       (void)cpu_counter();
+       (void)bus_space_read_4(sc->sc_memt, sc->sc_memh, HPET_MCOUNT_LO);
+       hd0 = bus_space_read_4(sc->sc_memt, sc->sc_memh, HPET_MCOUNT_LO);
+       td0 = cpu_counter();
+       splx(s);
+
+       /*
+        * Wait 1000000 HPET ticks (typically 50..100ms).
+        *
+        * This interval can produce an error of 1ppm (a few kHz
+        * in estimated TSC frequency), however the HPET timer is
+        * allowed to drift +/- 500ppm in that interval.
+        *
+        */
+       hpet_delay(sc->sc_period / 1000);
 
        /*
         * Determine TSC freq by comparing how far the TSC and HPET have
-        * advanced since attach time.  Take the cost of reading HPET
-        * register into account and round result to the nearest 1000.
+        * advanced and round result to the nearest 1000.
         */
        s = splhigh();
        (void)cpu_counter();
+       (void)bus_space_read_4(sc->sc_memt, sc->sc_memh, HPET_MCOUNT_LO);
        hd = bus_space_read_4(sc->sc_memt, sc->sc_memh, HPET_MCOUNT_LO);
        td = cpu_counter();
        splx(s);
-       hd -= hpet_attach_val;
-       val = ((uint64_t)hd * sc->sc_period - sc->sc_adj) / 100000000;
-       freq = (td - hpet_attach_tsc) * 10000000 / val;
+
+       val = (uint64_t)(hd - hd0) * sc->sc_period / 100000000;
+       freq = (td - td0) * 10000000 / val;
        return rounddown(freq + 500, 1000);
 }
 



Home | Main Index | Thread Index | Old Index