Hi,

There is interest in keeping support for booting with an RTC running
at an offset from UTC.  The existing DST/TIMEZONE options(4) do not
cooperate with KARL.

So, here's a new sysctl(2) to bridge the gap: kern.utc_offset.

It's an integer representing the RTC's offset from UTC in minutes East
of UTC+0.  I've decided to flip from TIMEZONE's "minutes West" to
"minutes East" because timezone offsets are canonically expressed this
way.  For instance, Japanese Standard Time (JST) is UTC+0900, so the
corresponding kern.utc_offset is 540.  Newfoundland Daylight Time is
allegedly UTC-0230, so the corresponding kern.utc_offset is -150.

The kernel clock is adjusted the moment this is set, as if the current
time on the clock is fresh from the RTC.  To ensure that this assumption
is correct this value should be set in early boot from sysctl.conf(5).
This will apply the correction and jump the kernel clock *before* ntpd(8)
and cron(8) start.

To encourage people to only set this value in early boot it is locked
when kern.securelevel is greater than zero.

Valid inputs range from UTC-2400 to UTC+2400.  Timezones in current
use range from UTC-1200 to UTC+1400.  There is room for timezone
changes in the future without allowing the operator to do something
obviously dumb.

I've added documentation to sysctl.2 and securelevel.7.  The sysctl.2
bits could be a bit better... or maybe briefer?  Still mulling it over.
Should I mention that it ought to be set from sysctl.conf(5)?  I'm also
fumbling with the phrase "in UTC mode".  It isn't quite what I want to
say.

jmc: any ideas?

The FAQ entry on multibooting will need to be updated too.  That will
happen in a separate diff.  I'll talk to the relevant people to sort
it out.

--

ok?

-Scott

Index: lib/libc/sys/sysctl.2
===================================================================
RCS file: /cvs/src/lib/libc/sys/sysctl.2,v
retrieving revision 1.29
diff -u -p -r1.29 sysctl.2
--- lib/libc/sys/sysctl.2       11 Aug 2019 16:04:23 -0000      1.29
+++ lib/libc/sys/sysctl.2       20 Aug 2019 14:17:51 -0000
@@ -488,6 +488,7 @@ information.
 .It Dv KERN_TIMECOUNTER Ta "node" Ta "not applicable"
 .It Dv KERN_TTY Ta "node" Ta "not applicable"
 .It Dv KERN_TTYCOUNT Ta "integer" Ta "no"
+.It Dv KERN_UTC_OFFSET Ta "integer" Ta "yes"
 .It Dv KERN_VERSION Ta "string" Ta "no"
 .It Dv KERN_WATCHDOG Ta "node" Ta "not applicable"
 .It Dv KERN_WITNESS Ta "node" Ta "not applicable"
@@ -1079,6 +1080,22 @@ Returns the number of input characters i
 Number of available
 .Xr tty 4
 devices.
+.It Dv KERN_UTC_OFFSET Pq Va kern.utc_offset
+The real-time clock's
+.Pq RTC
+offset from
+Coordinated Universal Time
+.Pq UTC
+expressed as minutes East of UTC+0.
+When set,
+time read from the RTC is adjusted to remove the offset
+and time written to the RTC is adjusted to reapply it.
+This may simplify multibooting with an operating system that
+does not run the RTC in UTC mode.
+When running with a
+.Xr securelevel 7
+greater than 0,
+this variable may not be changed.
 .It Dv KERN_VERSION Pq Va kern.version
 The system version string.
 .It Dv KERN_WATCHDOG Pq Va kern.watchdog
Index: share/man/man7/securelevel.7
===================================================================
RCS file: /cvs/src/share/man/man7/securelevel.7,v
retrieving revision 1.30
diff -u -p -r1.30 securelevel.7
--- share/man/man7/securelevel.7        12 Sep 2017 02:22:15 -0000      1.30
+++ share/man/man7/securelevel.7        20 Aug 2019 14:17:51 -0000
@@ -76,6 +76,7 @@ the
 .Va fs.posix.setuid ,
 .Va hw.allowpowerdown ,
 .Va kern.allowkmem ,
+.Va kern.utc_offset ,
 .Va net.inet.ip.sourceroute ,
 and
 .Va machdep.kbdreset
Index: sys/arch/amd64/isa/clock.c
===================================================================
RCS file: /cvs/src/sys/arch/amd64/isa/clock.c,v
retrieving revision 1.30
diff -u -p -r1.30 clock.c
--- sys/arch/amd64/isa/clock.c  19 Jul 2019 14:50:43 -0000      1.30
+++ sys/arch/amd64/isa/clock.c  20 Aug 2019 14:17:51 -0000
@@ -475,9 +475,7 @@ inittodr(time_t base)
        dt.dt_mon = bcdtobin(rtclk[MC_MONTH]);
        dt.dt_year = clock_expandyear(bcdtobin(rtclk[MC_YEAR]));
 
-       ts.tv_sec = clock_ymdhms_to_secs(&dt) + tz.tz_minuteswest * 60;
-       if (tz.tz_dsttime)
-               ts.tv_sec -= 3600;
+       ts.tv_sec = clock_ymdhms_to_secs(&dt) - utc_offset;
 
        if (base != 0 && base < ts.tv_sec - 5*SECYR)
                printf("WARNING: file system time much less than clock time\n");
@@ -506,7 +504,7 @@ resettodr(void)
 {
        mc_todregs rtclk;
        struct clock_ymdhms dt;
-       int century, diff, s;
+       int century, s;
 
        /*
         * We might have been called by boot() due to a crash early
@@ -520,10 +518,7 @@ resettodr(void)
                memset(&rtclk, 0, sizeof(rtclk));
        splx(s);
 
-       diff = tz.tz_minuteswest * 60;
-       if (tz.tz_dsttime)
-               diff -= 3600;
-       clock_secs_to_ymdhms(time_second - diff, &dt);
+       clock_secs_to_ymdhms(time_second + utc_offset, &dt);
 
        rtclk[MC_SEC] = bintobcd(dt.dt_sec);
        rtclk[MC_MIN] = bintobcd(dt.dt_min);
Index: sys/arch/i386/isa/clock.c
===================================================================
RCS file: /cvs/src/sys/arch/i386/isa/clock.c,v
retrieving revision 1.54
diff -u -p -r1.54 clock.c
--- sys/arch/i386/isa/clock.c   23 May 2019 19:00:52 -0000      1.54
+++ sys/arch/i386/isa/clock.c   20 Aug 2019 14:17:51 -0000
@@ -619,9 +619,7 @@ inittodr(time_t base)
        dt.dt_mon = hexdectodec(rtclk[MC_MONTH]);
        dt.dt_year = clock_expandyear(hexdectodec(rtclk[MC_YEAR]));
 
-       ts.tv_sec = clock_ymdhms_to_secs(&dt) + tz.tz_minuteswest * 60;
-       if (tz.tz_dsttime)
-               ts.tv_sec -= 3600;
+       ts.tv_sec = clock_ymdhms_to_secs(&dt) - utc_offset;
 
        if (base < ts.tv_sec - 5*SECYR)
                printf("WARNING: file system time much less than clock time\n");
@@ -666,10 +664,7 @@ resettodr(void)
                bzero(&rtclk, sizeof(rtclk));
        splx(s);
 
-       diff = tz.tz_minuteswest * 60;
-       if (tz.tz_dsttime)
-               diff -= 3600;
-       clock_secs_to_ymdhms(time_second - diff, &dt);
+       clock_secs_to_ymdhms(time_second + utc_offset, &dt);
 
        rtclk[MC_SEC] = dectohexdec(dt.dt_sec);
        rtclk[MC_MIN] = dectohexdec(dt.dt_min);
Index: sys/arch/macppc/macppc/clock.c
===================================================================
RCS file: /cvs/src/sys/arch/macppc/macppc/clock.c,v
retrieving revision 1.40
diff -u -p -r1.40 clock.c
--- sys/arch/macppc/macppc/clock.c      13 Jun 2015 07:16:36 -0000      1.40
+++ sys/arch/macppc/macppc/clock.c      20 Aug 2019 14:17:51 -0000
@@ -124,10 +124,7 @@ inittodr(time_t base)
        } else {
                int deltat;
 
-               tv.tv_sec += tz.tz_minuteswest * 60;
-               if (tz.tz_dsttime)
-                       tv.tv_sec -= 3600;
-
+               tv.tv_sec -= utc_offset;
                deltat = tv.tv_sec - base;
 
                if (deltat < 0)
@@ -167,13 +164,8 @@ resettodr(void)
 
        microtime(&tv);
 
-       if (time_write != NULL) {
-               tv.tv_sec -= tz.tz_minuteswest * 60;
-               if (tz.tz_dsttime) {
-                       tv.tv_sec += 3600;
-               }
-               (*time_write)(tv.tv_sec);
-       }
+       if (time_write != NULL)
+               (*time_write)(tv.tv_sec + utc_offset);
 }
 
 void
Index: sys/conf/param.c
===================================================================
RCS file: /cvs/src/sys/conf/param.c,v
retrieving revision 1.43
diff -u -p -r1.43 param.c
--- sys/conf/param.c    2 Aug 2019 02:17:35 -0000       1.43
+++ sys/conf/param.c    20 Aug 2019 14:17:51 -0000
@@ -83,6 +83,7 @@ int   tick = 1000000 / HZ;
 int    tick_nsec = 1000000000 / HZ;
 int    tickadj = 240000 / (60 * HZ);           /* can adjust 240ms in 60s */
 struct timezone tz  __attribute__ ((section(".data"))) = { TIMEZONE, DST };
+int    utc_offset = 0;
 #define        NPROCESS (30 + 16 * MAXUSERS)
 #define        NTEXT (80 + NPROCESS / 8)               /* actually the object 
cache */
 #define        NVNODE (NPROCESS * 2 + NTEXT + 100)
Index: sys/kern/kern_sysctl.c
===================================================================
RCS file: /cvs/src/sys/kern/kern_sysctl.c,v
retrieving revision 1.365
diff -u -p -r1.365 kern_sysctl.c
--- sys/kern/kern_sysctl.c      5 Aug 2019 08:35:59 -0000       1.365
+++ sys/kern/kern_sysctl.c      20 Aug 2019 14:17:52 -0000
@@ -142,6 +142,7 @@ int sysctl_cptime2(int *, u_int, void *,
 int sysctl_audio(int *, u_int, void *, size_t *, void *, size_t);
 #endif
 int sysctl_cpustats(int *, u_int, void *, size_t *, void *, size_t);
+int sysctl_utc_offset(void *, size_t *, void *, size_t);
 
 void fill_file(struct kinfo_file *, struct file *, struct filedesc *, int,
     struct vnode *, struct process *, struct proc *, struct socket *, int);
@@ -675,6 +676,8 @@ kern_sysctl(int *name, u_int namelen, vo
 #endif
        case KERN_TIMEOUT_STATS:
                return (timeout_sysctl(oldp, oldlenp, newp, newlen));
+       case KERN_UTC_OFFSET:
+               return (sysctl_utc_offset(oldp, oldlenp, newp, newlen));
        default:
                return (EOPNOTSUPP);
        }
@@ -2458,4 +2461,35 @@ sysctl_cpustats(int *name, u_int namelen
                cs.cs_flags |= CPUSTATS_ONLINE;
 
        return (sysctl_rdstruct(oldp, oldlenp, newp, &cs, sizeof(cs)));
+}
+
+int
+sysctl_utc_offset(void *oldp, size_t *oldlenp, void *newp, size_t newlen)
+{
+       struct timespec adjusted, now;
+       int adjustment_seconds, error, new_offset_minutes, old_offset_minutes;
+
+       old_offset_minutes = utc_offset / 60;   /* seconds -> minutes */
+       if (securelevel > 0)
+               return sysctl_rdint(oldp, oldlenp, newp, old_offset_minutes);
+
+       new_offset_minutes = old_offset_minutes;
+       error = sysctl_int(oldp, oldlenp, newp, newlen, &new_offset_minutes);
+       if (error)
+               return error;
+       if (new_offset_minutes < -24 * 60 || new_offset_minutes > 24 * 60)
+               return EINVAL;
+       if (new_offset_minutes == old_offset_minutes)
+               return 0;
+
+       utc_offset = new_offset_minutes * 60;   /* minutes -> seconds */
+       adjustment_seconds = (new_offset_minutes - old_offset_minutes) * 60;
+
+       nanotime(&now);
+       adjusted = now;
+       adjusted.tv_sec -= adjustment_seconds;
+       tc_setrealtimeclock(&adjusted);
+       resettodr();
+
+       return 0;
 }
Index: sys/sys/kernel.h
===================================================================
RCS file: /cvs/src/sys/sys/kernel.h,v
retrieving revision 1.20
diff -u -p -r1.20 kernel.h
--- sys/sys/kernel.h    2 Aug 2019 02:17:35 -0000       1.20
+++ sys/sys/kernel.h    20 Aug 2019 14:17:52 -0000
@@ -48,6 +48,7 @@ extern int domainnamelen;
 
 /* 1.2 */
 extern struct timezone tz;                     /* XXX */
+extern int utc_offset;         /* seconds east of UTC */
 
 extern int tick;               /* usec per tick (1000000 / hz) */
 extern int tick_nsec;          /* nsec per tick */
Index: sys/sys/sysctl.h
===================================================================
RCS file: /cvs/src/sys/sys/sysctl.h,v
retrieving revision 1.194
diff -u -p -r1.194 sysctl.h
--- sys/sys/sysctl.h    12 Jul 2019 00:04:59 -0000      1.194
+++ sys/sys/sysctl.h    20 Aug 2019 14:17:52 -0000
@@ -188,7 +188,8 @@ struct ctlname {
 #define        KERN_CPUSTATS           85      /* struct: cpu statistics */
 #define        KERN_PFSTATUS           86      /* struct: pf status and stats 
*/
 #define        KERN_TIMEOUT_STATS      87      /* struct: timeout status and 
stats */
-#define        KERN_MAXID              88      /* number of valid kern ids */
+#define        KERN_UTC_OFFSET         88      /* int: adjust RTC time to UTC 
*/
+#define        KERN_MAXID              89      /* number of valid kern ids */
 
 #define        CTL_KERN_NAMES { \
        { 0, 0 }, \
@@ -279,6 +280,7 @@ struct ctlname {
        { "cpustats", CTLTYPE_STRUCT }, \
        { "pfstatus", CTLTYPE_STRUCT }, \
        { "timeout_stats", CTLTYPE_STRUCT }, \
+       { "utc_offset", CTLTYPE_INT }, \
 }
 
 /*
@@ -433,8 +435,8 @@ struct kinfo_proc {
 
        int64_t p_uvalid;               /* CHAR: following p_u* members from 
struct user are valid */
                                        /* XXX 64 bits for alignment */
-       u_int64_t p_ustart_sec;         /* STRUCT TIMEVAL: starting time. */
-       u_int32_t p_ustart_usec;        /* STRUCT TIMEVAL: starting time. */
+       u_int64_t p_ustart_sec;         /* STRUCT TIMEVAL: starting uptime. */
+       u_int32_t p_ustart_usec;        /* STRUCT TIMEVAL: starting uptime. */
 
        u_int32_t p_uutime_sec;         /* STRUCT TIMEVAL: user time. */
        u_int32_t p_uutime_usec;        /* STRUCT TIMEVAL: user time. */

Reply via email to