The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/kern/kern_todr.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*      $NetBSD: kern_todr.c,v 1.47 2021/04/03 12:06:53 simonb Exp $    */
    2 
    3 /*-
    4  * Copyright (c) 2020 The NetBSD Foundation, Inc.
    5  * All rights reserved.
    6  *
    7  * This code is derived from software contributed to The NetBSD Foundation
    8  * by Jason R. Thorpe.
    9  *
   10  * Redistribution and use in source and binary forms, with or without
   11  * modification, are permitted provided that the following conditions
   12  * are met:
   13  * 1. Redistributions of source code must retain the above copyright
   14  *    notice, this list of conditions and the following disclaimer.
   15  * 2. Redistributions in binary form must reproduce the above copyright
   16  *    notice, this list of conditions and the following disclaimer in the
   17  *    documentation and/or other materials provided with the distribution.
   18  *
   19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
   20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
   21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
   22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
   23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   29  * POSSIBILITY OF SUCH DAMAGE.
   30  */
   31 
   32 /*
   33  * Copyright (c) 1988 University of Utah.
   34  * Copyright (c) 1992, 1993
   35  *      The Regents of the University of California.  All rights reserved.
   36  *
   37  * This code is derived from software contributed to Berkeley by
   38  * the Systems Programming Group of the University of Utah Computer
   39  * Science Department and Ralph Campbell.
   40  *
   41  * Redistribution and use in source and binary forms, with or without
   42  * modification, are permitted provided that the following conditions
   43  * are met:
   44  * 1. Redistributions of source code must retain the above copyright
   45  *    notice, this list of conditions and the following disclaimer.
   46  * 2. Redistributions in binary form must reproduce the above copyright
   47  *    notice, this list of conditions and the following disclaimer in the
   48  *    documentation and/or other materials provided with the distribution.
   49  * 3. Neither the name of the University nor the names of its contributors
   50  *    may be used to endorse or promote products derived from this software
   51  *    without specific prior written permission.
   52  *
   53  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
   54  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   55  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   56  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   57  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   58  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   59  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   60  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   61  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   62  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   63  * SUCH DAMAGE.
   64  *
   65  * from: Utah Hdr: clock.c 1.18 91/01/21
   66  *
   67  *      @(#)clock.c     8.1 (Berkeley) 6/10/93
   68  */
   69 
   70 #include "opt_todr.h"
   71 
   72 #include <sys/cdefs.h>
   73 __KERNEL_RCSID(0, "$NetBSD: kern_todr.c,v 1.47 2021/04/03 12:06:53 simonb Exp $");
   74 
   75 #include <sys/param.h>
   76 #include <sys/kernel.h>
   77 #include <sys/systm.h>
   78 #include <sys/device.h>
   79 #include <sys/timetc.h>
   80 #include <sys/intr.h>
   81 #include <sys/rndsource.h>
   82 #include <sys/mutex.h>
   83 
   84 #include <dev/clock_subr.h>     /* hmm.. this should probably move to sys */
   85 
   86 static int todr_gettime(todr_chip_handle_t, struct timeval *);
   87 static int todr_settime(todr_chip_handle_t, struct timeval *);
   88 
   89 static kmutex_t todr_mutex;
   90 static todr_chip_handle_t todr_handle;
   91 static bool todr_initialized;
   92 
   93 /* The minimum reasonable RTC date before preposterousness */
   94 #define PREPOSTEROUS_YEARS      (2021 - POSIX_BASE_YEAR)
   95 
   96 /*
   97  * todr_init:
   98  *      Initialize TOD clock data.
   99  */
  100 void
  101 todr_init(void)
  102 {
  103 
  104         mutex_init(&todr_mutex, MUTEX_DEFAULT, IPL_NONE);
  105         todr_initialized = true;
  106 }
  107 
  108 /*
  109  * todr_lock:
  110  *      Acquire the TODR lock.
  111  */
  112 void
  113 todr_lock(void)
  114 {
  115 
  116         mutex_enter(&todr_mutex);
  117 }
  118 
  119 /*
  120  * todr_unlock:
  121  *      Release the TODR lock.
  122  */
  123 void
  124 todr_unlock(void)
  125 {
  126 
  127         mutex_exit(&todr_mutex);
  128 }
  129 
  130 /*
  131  * todr_lock_owned:
  132  *      Return true if the current thread owns the TODR lock.
  133  *      This is to be used by diagnostic assertions only.
  134  */
  135 bool
  136 todr_lock_owned(void)
  137 {
  138 
  139         return mutex_owned(&todr_mutex) ? true : false;
  140 }
  141 
  142 /*
  143  * todr_attach:
  144  *      Attach the clock device to todr_handle.
  145  */
  146 void
  147 todr_attach(todr_chip_handle_t todr)
  148 {
  149 
  150         /*
  151          * todr_init() is called very early in main(), but this is
  152          * here to catch a case where todr_attach() is called before
  153          * main().
  154          */
  155         KASSERT(todr_initialized);
  156 
  157         todr_lock();
  158         if (todr_handle) {
  159                 todr_unlock();
  160                 printf("todr_attach: TOD already configured\n");
  161                 return;
  162         }
  163         todr_handle = todr;
  164         todr_unlock();
  165 }
  166 
  167 static bool timeset = false;
  168 
  169 /*
  170  * todr_set_systime:
  171  *      Set up the system's time.  The "base" argument is a best-guess
  172  *      close-enough value to use if the TOD clock is unavailable or
  173  *      contains garbage.  Must be called with the TODR lock held.
  174  */
  175 void
  176 todr_set_systime(time_t base)
  177 {
  178         bool badbase = false;
  179         bool waszero = (base == 0);
  180         bool goodtime = false;
  181         bool badrtc = false;
  182         struct timespec ts;
  183         struct timeval tv;
  184 
  185         KASSERT(todr_lock_owned());
  186 
  187         rnd_add_data(NULL, &base, sizeof(base), 0);
  188 
  189         if (base < 5 * SECS_PER_COMMON_YEAR) {
  190                 struct clock_ymdhms basedate;
  191 
  192                 /*
  193                  * If base is 0, assume filesystem time is just unknown
  194                  * instead of preposterous. Don't bark.
  195                  */
  196                 if (base != 0)
  197                         printf("WARNING: preposterous time in file system\n");
  198                 /* not going to use it anyway, if the chip is readable */
  199                 basedate.dt_year = 2010;
  200                 basedate.dt_mon = 1;
  201                 basedate.dt_day = 1;
  202                 basedate.dt_hour = 12;
  203                 basedate.dt_min = 0;
  204                 basedate.dt_sec = 0;
  205                 base = clock_ymdhms_to_secs(&basedate);
  206                 badbase = true;
  207         }
  208 
  209         /*
  210          * Some ports need to be supplied base in order to fabricate a time_t.
  211          */
  212         if (todr_handle)
  213                 todr_handle->base_time = base;
  214 
  215         memset(&tv, 0, sizeof(tv));
  216 
  217         if ((todr_handle == NULL) ||
  218             (todr_gettime(todr_handle, &tv) != 0) ||
  219             (tv.tv_sec < (PREPOSTEROUS_YEARS * SECS_PER_COMMON_YEAR))) {
  220 
  221                 if (todr_handle != NULL)
  222                         printf("WARNING: preposterous TOD clock time\n");
  223                 else
  224                         printf("WARNING: no TOD clock present\n");
  225                 badrtc = true;
  226         } else {
  227                 time_t deltat = tv.tv_sec - base;
  228 
  229                 if (deltat < 0)
  230                         deltat = -deltat;
  231 
  232                 if (!badbase && deltat >= 2 * SECS_PER_DAY) {
  233                         
  234                         if (tv.tv_sec < base) {
  235                                 /*
  236                                  * The clock should never go backwards
  237                                  * relative to filesystem time.  If it
  238                                  * does by more than the threshold,
  239                                  * believe the filesystem.
  240                                  */
  241                                 printf("WARNING: clock lost %" PRId64 " days\n",
  242                                     deltat / SECS_PER_DAY);
  243                                 badrtc = true;
  244                         } else {
  245                                 aprint_verbose("WARNING: clock gained %" PRId64
  246                                     " days\n", deltat / SECS_PER_DAY);
  247                                 goodtime = true;
  248                         }
  249                 } else {
  250                         goodtime = true;
  251                 }
  252 
  253                 rnd_add_data(NULL, &tv, sizeof(tv), 0);
  254         }
  255 
  256         /* if the rtc time is bad, use the filesystem time */
  257         if (badrtc) {
  258                 if (badbase) {
  259                         printf("WARNING: using default initial time\n");
  260                 } else {
  261                         printf("WARNING: using filesystem time\n");
  262                 }
  263                 tv.tv_sec = base;
  264                 tv.tv_usec = 0;
  265         }
  266 
  267         timeset = true;
  268 
  269         ts.tv_sec = tv.tv_sec;
  270         ts.tv_nsec = tv.tv_usec * 1000;
  271         tc_setclock(&ts);
  272 
  273         if (waszero || goodtime)
  274                 return;
  275 
  276         printf("WARNING: CHECK AND RESET THE DATE!\n");
  277 }
  278 
  279 /*
  280  * todr_save_systime:
  281  *      Save the current system time back to the TOD clock.
  282  *      Must be called with the TODR lock held.
  283  */
  284 void
  285 todr_save_systime(void)
  286 {
  287         struct timeval tv;
  288 
  289         KASSERT(todr_lock_owned());
  290 
  291         /*
  292          * We might have been called by boot() due to a crash early
  293          * on.  Don't reset the clock chip if we don't know what time
  294          * it is.
  295          */
  296         if (!timeset)
  297                 return;
  298 
  299         getmicrotime(&tv);
  300 
  301         if (tv.tv_sec == 0)
  302                 return;
  303 
  304         if (todr_handle)
  305                 if (todr_settime(todr_handle, &tv) != 0)
  306                         printf("Cannot set TOD clock time\n");
  307 }
  308 
  309 /*
  310  * inittodr:
  311  *      Legacy wrapper around todr_set_systime().
  312  */
  313 void
  314 inittodr(time_t base)
  315 {
  316 
  317         todr_lock();
  318         todr_set_systime(base);
  319         todr_unlock();
  320 }
  321 
  322 /*
  323  * resettodr:
  324  *      Legacy wrapper around todr_save_systime().
  325  */
  326 void
  327 resettodr(void)
  328 {
  329 
  330         /*
  331          * If we're shutting down, we don't want to get stuck in case
  332          * someone was already fiddling with the TOD clock.
  333          */
  334         if (shutting_down) {
  335                 if (mutex_tryenter(&todr_mutex) == 0) {
  336                         printf("WARNING: Cannot set TOD clock time (busy)\n");
  337                         return;
  338                 }
  339         } else {
  340                 todr_lock();
  341         }
  342         todr_save_systime();
  343         todr_unlock();
  344 }
  345 
  346 #ifdef  TODR_DEBUG
  347 static void
  348 todr_debug(const char *prefix, int rv, struct clock_ymdhms *dt,
  349     struct timeval *tvp)
  350 {
  351         struct timeval tv_val;
  352         struct clock_ymdhms dt_val;
  353 
  354         if (dt == NULL) {
  355                 clock_secs_to_ymdhms(tvp->tv_sec, &dt_val);
  356                 dt = &dt_val;
  357         }
  358         if (tvp == NULL) {
  359                 tvp = &tv_val;
  360                 tvp->tv_sec = clock_ymdhms_to_secs(dt);
  361                 tvp->tv_usec = 0;
  362         }
  363         printf("%s: rv = %d\n", prefix, rv);
  364         printf("%s: rtc_offset = %d\n", prefix, rtc_offset);
  365         printf("%s: %4u/%02u/%02u %02u:%02u:%02u, (wday %d) (epoch %u.%06u)\n",
  366             prefix,
  367             (unsigned)dt->dt_year, dt->dt_mon, dt->dt_day,
  368             dt->dt_hour, dt->dt_min, dt->dt_sec,
  369             dt->dt_wday, (unsigned)tvp->tv_sec, (unsigned)tvp->tv_usec);
  370 }
  371 #else   /* !TODR_DEBUG */
  372 #define todr_debug(prefix, rv, dt, tvp)
  373 #endif  /* TODR_DEBUG */
  374 
  375 static int
  376 todr_wenable(todr_chip_handle_t todr, int onoff)
  377 {
  378 
  379         if (todr->todr_setwen != NULL)
  380                 return todr->todr_setwen(todr, onoff);
  381         
  382         return 0;
  383 }
  384 
  385 #define ENABLE_TODR_WRITES()                                            \
  386 do {                                                                    \
  387         if ((rv = todr_wenable(tch, 1)) != 0) {                         \
  388                 printf("%s: cannot enable TODR writes\n", __func__);    \
  389                 return rv;                                              \
  390         }                                                               \
  391 } while (/*CONSTCOND*/0)
  392 
  393 #define DISABLE_TODR_WRITES()                                           \
  394 do {                                                                    \
  395         if (todr_wenable(tch, 0) != 0)                                  \
  396                 printf("%s: WARNING: cannot disable TODR writes\n",     \
  397                     __func__);                                          \
  398 } while (/*CONSTCOND*/0)
  399 
  400 static int
  401 todr_gettime(todr_chip_handle_t tch, struct timeval *tvp)
  402 {
  403         int rv;
  404 
  405         /*
  406          * Write-enable is used even when reading the TODR because
  407          * writing to registers may be required in order to do so.
  408          */
  409 
  410         if (tch->todr_gettime) {
  411                 ENABLE_TODR_WRITES();
  412                 rv = tch->todr_gettime(tch, tvp);
  413                 DISABLE_TODR_WRITES();
  414                 /*
  415                  * Some unconverted ports have their own references to
  416                  * rtc_offset.   A converted port must not do that.
  417                  */
  418                 if (rv == 0)
  419                         tvp->tv_sec += rtc_offset * 60;
  420                 todr_debug("TODR-GET-SECS", rv, NULL, tvp);
  421                 return rv;
  422         } else if (tch->todr_gettime_ymdhms) {
  423                 struct clock_ymdhms dt = { 0 };
  424                 ENABLE_TODR_WRITES();
  425                 rv = tch->todr_gettime_ymdhms(tch, &dt);
  426                 DISABLE_TODR_WRITES();
  427                 todr_debug("TODR-GET-YMDHMS", rv, &dt, NULL);
  428                 if (rv)
  429                         return rv;
  430 
  431                 /*
  432                  * Simple sanity checks.  Note that this includes a
  433                  * value for clocks that can return a leap second.
  434                  * Note that we don't support double leap seconds,
  435                  * since this was apparently an error/misunderstanding
  436                  * on the part of the ISO C committee, and can never
  437                  * actually occur.  If your clock issues us a double
  438                  * leap second, it must be broken.  Ultimately, you'd
  439                  * have to be trying to read time at precisely that
  440                  * instant to even notice, so even broken clocks will
  441                  * work the vast majority of the time.  In such a case
  442                  * it is recommended correction be applied in the
  443                  * clock driver.
  444                  */
  445                 if (dt.dt_mon < 1 || dt.dt_mon > 12 ||
  446                     dt.dt_day < 1 || dt.dt_day > 31 ||
  447                     dt.dt_hour > 23 || dt.dt_min > 59 || dt.dt_sec > 60) {
  448                         return EINVAL;
  449                 }
  450                 tvp->tv_sec = clock_ymdhms_to_secs(&dt) + rtc_offset * 60;
  451                 tvp->tv_usec = 0;
  452                 return tvp->tv_sec < 0 ? EINVAL : 0;
  453         }
  454 
  455         return ENXIO;
  456 }
  457 
  458 static int
  459 todr_settime(todr_chip_handle_t tch, struct timeval *tvp)
  460 {
  461         int rv;
  462 
  463         if (tch->todr_settime) {
  464                 struct timeval copy = *tvp;
  465                 copy.tv_sec -= rtc_offset * 60;
  466                 ENABLE_TODR_WRITES();
  467                 rv = tch->todr_settime(tch, &copy);
  468                 DISABLE_TODR_WRITES();
  469                 todr_debug("TODR-SET-SECS", rv, NULL, tvp);
  470                 return rv;
  471         } else if (tch->todr_settime_ymdhms) {
  472                 struct clock_ymdhms dt;
  473                 time_t sec = tvp->tv_sec - rtc_offset * 60;
  474                 if (tvp->tv_usec >= 500000)
  475                         sec++;
  476                 clock_secs_to_ymdhms(sec, &dt);
  477                 ENABLE_TODR_WRITES();
  478                 rv = tch->todr_settime_ymdhms(tch, &dt);
  479                 DISABLE_TODR_WRITES();
  480                 todr_debug("TODR-SET-YMDHMS", rv, &dt, NULL);
  481                 return rv;
  482         }
  483 
  484         return ENXIO;
  485 }

Cache object: da9f55709bfae7a456d73eaf79d333db


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.