1 /* $NetBSD: kern_todr.c,v 1.39 2015/04/13 16:36:54 riastradh 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.39 2015/04/13 16:36:54 riastradh 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 if ((todr_handle == NULL) || 117 (todr_gettime(todr_handle, &tv) != 0) || 118 (tv.tv_sec < (25 * SECS_PER_COMMON_YEAR))) { 119 120 if (todr_handle != NULL) 121 printf("WARNING: preposterous TOD clock time\n"); 122 else 123 printf("WARNING: no TOD clock present\n"); 124 badrtc = true; 125 } else { 126 time_t deltat = tv.tv_sec - base; 127 128 if (deltat < 0) 129 deltat = -deltat; 130 131 if (!badbase && deltat >= 2 * SECS_PER_DAY) { 132 133 if (tv.tv_sec < base) { 134 /* 135 * The clock should never go backwards 136 * relative to filesystem time. If it 137 * does by more than the threshold, 138 * believe the filesystem. 139 */ 140 printf("WARNING: clock lost %" PRId64 " days\n", 141 deltat / SECS_PER_DAY); 142 badrtc = true; 143 } else { 144 aprint_verbose("WARNING: clock gained %" PRId64 145 " days\n", deltat / SECS_PER_DAY); 146 goodtime = true; 147 } 148 } else { 149 goodtime = true; 150 } 151 152 rnd_add_data(NULL, &tv, sizeof(tv), 0); 153 } 154 155 /* if the rtc time is bad, use the filesystem time */ 156 if (badrtc) { 157 if (badbase) { 158 printf("WARNING: using default initial time\n"); 159 } else { 160 printf("WARNING: using filesystem time\n"); 161 } 162 tv.tv_sec = base; 163 tv.tv_usec = 0; 164 } 165 166 timeset = true; 167 168 ts.tv_sec = tv.tv_sec; 169 ts.tv_nsec = tv.tv_usec * 1000; 170 s = splclock(); 171 tc_setclock(&ts); 172 splx(s); 173 174 if (waszero || goodtime) 175 return; 176 177 printf("WARNING: CHECK AND RESET THE DATE!\n"); 178 } 179 180 /* 181 * Reset the TODR based on the time value; used when the TODR 182 * has a preposterous value and also when the time is reset 183 * by the stime system call. Also called when the TODR goes past 184 * TODRZERO + 100*(SECS_PER_COMMON_YEAR+2*SECS_PER_DAY) 185 * (e.g. on Jan 2 just after midnight) to wrap the TODR around. 186 */ 187 void 188 resettodr(void) 189 { 190 struct timeval tv; 191 192 /* 193 * We might have been called by boot() due to a crash early 194 * on. Don't reset the clock chip if we don't know what time 195 * it is. 196 */ 197 if (!timeset) 198 return; 199 200 getmicrotime(&tv); 201 202 if (tv.tv_sec == 0) 203 return; 204 205 if (todr_handle) 206 if (todr_settime(todr_handle, &tv) != 0) 207 printf("Cannot set TOD clock time\n"); 208 } 209 210 #ifdef TODR_DEBUG 211 static void 212 todr_debug(const char *prefix, int rv, struct clock_ymdhms *dt, 213 struct timeval *tvp) 214 { 215 struct timeval tv_val; 216 struct clock_ymdhms dt_val; 217 218 if (dt == NULL) { 219 clock_secs_to_ymdhms(tvp->tv_sec, &dt_val); 220 dt = &dt_val; 221 } 222 if (tvp == NULL) { 223 tvp = &tv_val; 224 tvp->tv_sec = clock_ymdhms_to_secs(dt); 225 tvp->tv_usec = 0; 226 } 227 printf("%s: rv = %d\n", prefix, rv); 228 printf("%s: rtc_offset = %d\n", prefix, rtc_offset); 229 printf("%s: %4u/%02u/%02u %02u:%02u:%02u, (wday %d) (epoch %u.%06u)\n", 230 prefix, 231 (unsigned)dt->dt_year, dt->dt_mon, dt->dt_day, 232 dt->dt_hour, dt->dt_min, dt->dt_sec, 233 dt->dt_wday, (unsigned)tvp->tv_sec, (unsigned)tvp->tv_usec); 234 } 235 #else /* !TODR_DEBUG */ 236 #define todr_debug(prefix, rv, dt, tvp) 237 #endif /* TODR_DEBUG */ 238 239 240 int 241 todr_gettime(todr_chip_handle_t tch, struct timeval *tvp) 242 { 243 struct clock_ymdhms dt; 244 int rv; 245 246 if (tch->todr_gettime) { 247 rv = tch->todr_gettime(tch, tvp); 248 /* 249 * Some unconverted ports have their own references to 250 * rtc_offset. A converted port must not do that. 251 */ 252 if (rv == 0) 253 tvp->tv_sec += rtc_offset * 60; 254 todr_debug("TODR-GET-SECS", rv, NULL, tvp); 255 return rv; 256 } else if (tch->todr_gettime_ymdhms) { 257 rv = tch->todr_gettime_ymdhms(tch, &dt); 258 todr_debug("TODR-GET-YMDHMS", rv, &dt, NULL); 259 if (rv) 260 return rv; 261 262 /* 263 * Simple sanity checks. Note that this includes a 264 * value for clocks that can return a leap second. 265 * Note that we don't support double leap seconds, 266 * since this was apparently an error/misunderstanding 267 * on the part of the ISO C committee, and can never 268 * actually occur. If your clock issues us a double 269 * leap second, it must be broken. Ultimately, you'd 270 * have to be trying to read time at precisely that 271 * instant to even notice, so even broken clocks will 272 * work the vast majority of the time. In such a case 273 * it is recommended correction be applied in the 274 * clock driver. 275 */ 276 if (dt.dt_mon < 1 || dt.dt_mon > 12 || 277 dt.dt_day < 1 || dt.dt_day > 31 || 278 dt.dt_hour > 23 || dt.dt_min > 59 || dt.dt_sec > 60) { 279 return EINVAL; 280 } 281 tvp->tv_sec = clock_ymdhms_to_secs(&dt) + rtc_offset * 60; 282 tvp->tv_usec = 0; 283 return tvp->tv_sec < 0 ? EINVAL : 0; 284 } 285 286 return ENXIO; 287 } 288 289 int 290 todr_settime(todr_chip_handle_t tch, struct timeval *tvp) 291 { 292 struct clock_ymdhms dt; 293 int rv; 294 295 if (tch->todr_settime) { 296 /* See comments above in gettime why this is ifdef'd */ 297 struct timeval copy = *tvp; 298 copy.tv_sec -= rtc_offset * 60; 299 rv = tch->todr_settime(tch, ©); 300 todr_debug("TODR-SET-SECS", rv, NULL, tvp); 301 return rv; 302 } else if (tch->todr_settime_ymdhms) { 303 time_t sec = tvp->tv_sec - rtc_offset * 60; 304 if (tvp->tv_usec >= 500000) 305 sec++; 306 clock_secs_to_ymdhms(sec, &dt); 307 rv = tch->todr_settime_ymdhms(tch, &dt); 308 todr_debug("TODR-SET-YMDHMS", rv, &dt, NULL); 309 return rv; 310 } else { 311 return ENXIO; 312 } 313 } 314