1 /* $NetBSD: kern_todr.c,v 1.40 2019/07/07 15:12:59 maxv Exp $ */ 2 3 /* 4 * Copyright (c) 1988 University of Utah. 5 * Copyright (c) 1992, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * This code is derived from software contributed to Berkeley by 9 * the Systems Programming Group of the University of Utah Computer 10 * Science Department and Ralph Campbell. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 3. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 * 36 * from: Utah Hdr: clock.c 1.18 91/01/21 37 * 38 * @(#)clock.c 8.1 (Berkeley) 6/10/93 39 */ 40 41 #include "opt_todr.h" 42 43 #include <sys/cdefs.h> 44 __KERNEL_RCSID(0, "$NetBSD: kern_todr.c,v 1.40 2019/07/07 15:12:59 maxv Exp $"); 45 46 #include <sys/param.h> 47 #include <sys/kernel.h> 48 #include <sys/systm.h> 49 #include <sys/device.h> 50 #include <sys/timetc.h> 51 #include <sys/intr.h> 52 #include <sys/rndsource.h> 53 54 #include <dev/clock_subr.h> /* hmm.. this should probably move to sys */ 55 56 static todr_chip_handle_t todr_handle = NULL; 57 58 /* 59 * Attach the clock device to todr_handle. 60 */ 61 void 62 todr_attach(todr_chip_handle_t todr) 63 { 64 65 if (todr_handle) { 66 printf("todr_attach: TOD already configured\n"); 67 return; 68 } 69 todr_handle = todr; 70 } 71 72 static bool timeset = false; 73 74 /* 75 * Set up the system's time, given a `reasonable' time value. 76 */ 77 void 78 inittodr(time_t base) 79 { 80 bool badbase = false; 81 bool waszero = (base == 0); 82 bool goodtime = false; 83 bool badrtc = false; 84 int s; 85 struct timespec ts; 86 struct timeval tv; 87 88 rnd_add_data(NULL, &base, sizeof(base), 0); 89 90 if (base < 5 * SECS_PER_COMMON_YEAR) { 91 struct clock_ymdhms basedate; 92 93 /* 94 * If base is 0, assume filesystem time is just unknown 95 * instead of preposterous. Don't bark. 96 */ 97 if (base != 0) 98 printf("WARNING: preposterous time in file system\n"); 99 /* not going to use it anyway, if the chip is readable */ 100 basedate.dt_year = 2010; 101 basedate.dt_mon = 1; 102 basedate.dt_day = 1; 103 basedate.dt_hour = 12; 104 basedate.dt_min = 0; 105 basedate.dt_sec = 0; 106 base = clock_ymdhms_to_secs(&basedate); 107 badbase = true; 108 } 109 110 /* 111 * Some ports need to be supplied base in order to fabricate a time_t. 112 */ 113 if (todr_handle) 114 todr_handle->base_time = base; 115 116 memset(&tv, 0, sizeof(tv)); 117 118 if ((todr_handle == NULL) || 119 (todr_gettime(todr_handle, &tv) != 0) || 120 (tv.tv_sec < (25 * SECS_PER_COMMON_YEAR))) { 121 122 if (todr_handle != NULL) 123 printf("WARNING: preposterous TOD clock time\n"); 124 else 125 printf("WARNING: no TOD clock present\n"); 126 badrtc = true; 127 } else { 128 time_t deltat = tv.tv_sec - base; 129 130 if (deltat < 0) 131 deltat = -deltat; 132 133 if (!badbase && deltat >= 2 * SECS_PER_DAY) { 134 135 if (tv.tv_sec < base) { 136 /* 137 * The clock should never go backwards 138 * relative to filesystem time. If it 139 * does by more than the threshold, 140 * believe the filesystem. 141 */ 142 printf("WARNING: clock lost %" PRId64 " days\n", 143 deltat / SECS_PER_DAY); 144 badrtc = true; 145 } else { 146 aprint_verbose("WARNING: clock gained %" PRId64 147 " days\n", deltat / SECS_PER_DAY); 148 goodtime = true; 149 } 150 } else { 151 goodtime = true; 152 } 153 154 rnd_add_data(NULL, &tv, sizeof(tv), 0); 155 } 156 157 /* if the rtc time is bad, use the filesystem time */ 158 if (badrtc) { 159 if (badbase) { 160 printf("WARNING: using default initial time\n"); 161 } else { 162 printf("WARNING: using filesystem time\n"); 163 } 164 tv.tv_sec = base; 165 tv.tv_usec = 0; 166 } 167 168 timeset = true; 169 170 ts.tv_sec = tv.tv_sec; 171 ts.tv_nsec = tv.tv_usec * 1000; 172 s = splclock(); 173 tc_setclock(&ts); 174 splx(s); 175 176 if (waszero || goodtime) 177 return; 178 179 printf("WARNING: CHECK AND RESET THE DATE!\n"); 180 } 181 182 /* 183 * Reset the TODR based on the time value; used when the TODR 184 * has a preposterous value and also when the time is reset 185 * by the stime system call. Also called when the TODR goes past 186 * TODRZERO + 100*(SECS_PER_COMMON_YEAR+2*SECS_PER_DAY) 187 * (e.g. on Jan 2 just after midnight) to wrap the TODR around. 188 */ 189 void 190 resettodr(void) 191 { 192 struct timeval tv; 193 194 /* 195 * We might have been called by boot() due to a crash early 196 * on. Don't reset the clock chip if we don't know what time 197 * it is. 198 */ 199 if (!timeset) 200 return; 201 202 getmicrotime(&tv); 203 204 if (tv.tv_sec == 0) 205 return; 206 207 if (todr_handle) 208 if (todr_settime(todr_handle, &tv) != 0) 209 printf("Cannot set TOD clock time\n"); 210 } 211 212 #ifdef TODR_DEBUG 213 static void 214 todr_debug(const char *prefix, int rv, struct clock_ymdhms *dt, 215 struct timeval *tvp) 216 { 217 struct timeval tv_val; 218 struct clock_ymdhms dt_val; 219 220 if (dt == NULL) { 221 clock_secs_to_ymdhms(tvp->tv_sec, &dt_val); 222 dt = &dt_val; 223 } 224 if (tvp == NULL) { 225 tvp = &tv_val; 226 tvp->tv_sec = clock_ymdhms_to_secs(dt); 227 tvp->tv_usec = 0; 228 } 229 printf("%s: rv = %d\n", prefix, rv); 230 printf("%s: rtc_offset = %d\n", prefix, rtc_offset); 231 printf("%s: %4u/%02u/%02u %02u:%02u:%02u, (wday %d) (epoch %u.%06u)\n", 232 prefix, 233 (unsigned)dt->dt_year, dt->dt_mon, dt->dt_day, 234 dt->dt_hour, dt->dt_min, dt->dt_sec, 235 dt->dt_wday, (unsigned)tvp->tv_sec, (unsigned)tvp->tv_usec); 236 } 237 #else /* !TODR_DEBUG */ 238 #define todr_debug(prefix, rv, dt, tvp) 239 #endif /* TODR_DEBUG */ 240 241 242 int 243 todr_gettime(todr_chip_handle_t tch, struct timeval *tvp) 244 { 245 struct clock_ymdhms dt; 246 int rv; 247 248 if (tch->todr_gettime) { 249 rv = tch->todr_gettime(tch, tvp); 250 /* 251 * Some unconverted ports have their own references to 252 * rtc_offset. A converted port must not do that. 253 */ 254 if (rv == 0) 255 tvp->tv_sec += rtc_offset * 60; 256 todr_debug("TODR-GET-SECS", rv, NULL, tvp); 257 return rv; 258 } else if (tch->todr_gettime_ymdhms) { 259 rv = tch->todr_gettime_ymdhms(tch, &dt); 260 todr_debug("TODR-GET-YMDHMS", rv, &dt, NULL); 261 if (rv) 262 return rv; 263 264 /* 265 * Simple sanity checks. Note that this includes a 266 * value for clocks that can return a leap second. 267 * Note that we don't support double leap seconds, 268 * since this was apparently an error/misunderstanding 269 * on the part of the ISO C committee, and can never 270 * actually occur. If your clock issues us a double 271 * leap second, it must be broken. Ultimately, you'd 272 * have to be trying to read time at precisely that 273 * instant to even notice, so even broken clocks will 274 * work the vast majority of the time. In such a case 275 * it is recommended correction be applied in the 276 * clock driver. 277 */ 278 if (dt.dt_mon < 1 || dt.dt_mon > 12 || 279 dt.dt_day < 1 || dt.dt_day > 31 || 280 dt.dt_hour > 23 || dt.dt_min > 59 || dt.dt_sec > 60) { 281 return EINVAL; 282 } 283 tvp->tv_sec = clock_ymdhms_to_secs(&dt) + rtc_offset * 60; 284 tvp->tv_usec = 0; 285 return tvp->tv_sec < 0 ? EINVAL : 0; 286 } 287 288 return ENXIO; 289 } 290 291 int 292 todr_settime(todr_chip_handle_t tch, struct timeval *tvp) 293 { 294 struct clock_ymdhms dt; 295 int rv; 296 297 if (tch->todr_settime) { 298 /* See comments above in gettime why this is ifdef'd */ 299 struct timeval copy = *tvp; 300 copy.tv_sec -= rtc_offset * 60; 301 rv = tch->todr_settime(tch, ©); 302 todr_debug("TODR-SET-SECS", rv, NULL, tvp); 303 return rv; 304 } else if (tch->todr_settime_ymdhms) { 305 time_t sec = tvp->tv_sec - rtc_offset * 60; 306 if (tvp->tv_usec >= 500000) 307 sec++; 308 clock_secs_to_ymdhms(sec, &dt); 309 rv = tch->todr_settime_ymdhms(tch, &dt); 310 todr_debug("TODR-SET-YMDHMS", rv, &dt, NULL); 311 return rv; 312 } else { 313 return ENXIO; 314 } 315 } 316