1 /* $NetBSD: kern_todr.c,v 1.52 2026/01/04 02:09:03 riastradh 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.52 2026/01/04 02:09:03 riastradh Exp $"); 74 75 #include <sys/param.h> 76 77 #include <sys/device.h> 78 #include <sys/device_calls.h> 79 #include <sys/intr.h> 80 #include <sys/kernel.h> 81 #include <sys/mutex.h> 82 #include <sys/rndsource.h> 83 #include <sys/sdt.h> 84 #include <sys/systm.h> 85 #include <sys/timetc.h> 86 87 #include <dev/clock_subr.h> /* hmm.. this should probably move to sys */ 88 89 static int todr_gettime(todr_chip_handle_t, struct timeval *); 90 static int todr_settime(todr_chip_handle_t, struct timeval *); 91 92 static kmutex_t todr_mutex; 93 static todr_chip_handle_t todr_handle; 94 static bool todr_initialized; 95 96 /* The minimum reasonable RTC date before preposterousness */ 97 #define PREPOSTEROUS_YEARS (2021 - POSIX_BASE_YEAR) 98 99 /* 100 * todr_init: 101 * Initialize TOD clock data. 102 */ 103 void 104 todr_init(void) 105 { 106 107 mutex_init(&todr_mutex, MUTEX_DEFAULT, IPL_NONE); 108 todr_initialized = true; 109 } 110 111 /* 112 * todr_lock: 113 * Acquire the TODR lock. 114 */ 115 void 116 todr_lock(void) 117 { 118 119 mutex_enter(&todr_mutex); 120 } 121 122 /* 123 * todr_unlock: 124 * Release the TODR lock. 125 */ 126 void 127 todr_unlock(void) 128 { 129 130 mutex_exit(&todr_mutex); 131 } 132 133 /* 134 * todr_lock_owned: 135 * Return true if the current thread owns the TODR lock. 136 * This is to be used by diagnostic assertions only. 137 */ 138 bool 139 todr_lock_owned(void) 140 { 141 142 return mutex_owned(&todr_mutex) ? true : false; 143 } 144 145 /* 146 * todr_should_skip: 147 * Evaluate if we should skip attaching the clock device 148 * specified by the todr handle. 149 */ 150 static bool 151 todr_should_skip(todr_chip_handle_t todr) 152 { 153 device_t dev = todr->todr_dev; 154 struct device_is_system_todr_args args = { }; 155 156 /* No basis for evaluation if no device backs the TODR. */ 157 if (dev == NULL) { 158 return false; 159 } 160 161 if (device_call(dev, DEVICE_IS_SYSTEM_TODR(&args)) != 0) { 162 /* Call is not supported; proceed as if we should attach. */ 163 return false; 164 } 165 166 if (args.result) { 167 /* This is the system TODR; proceed. */ 168 return false; 169 } 170 171 aprint_normal_dev(dev, "disabled (not system TODR)\n"); 172 return true; 173 } 174 175 /* 176 * todr_attach: 177 * Attach the clock device to todr_handle. 178 */ 179 void 180 todr_attach(todr_chip_handle_t todr) 181 { 182 183 /* 184 * todr_init() is called very early in main(), but this is 185 * here to catch a case where todr_attach() is called before 186 * main(). 187 */ 188 KASSERT(todr_initialized); 189 190 if (todr_should_skip(todr)) { 191 return; 192 } 193 194 todr_lock(); 195 if (todr_handle) { 196 todr_unlock(); 197 printf("todr_attach: TOD already configured\n"); 198 return; 199 } 200 todr_handle = todr; 201 todr_unlock(); 202 } 203 204 static bool timeset = false; 205 206 /* 207 * todr_set_systime: 208 * Set up the system's time. The "base" argument is a best-guess 209 * close-enough value to use if the TOD clock is unavailable or 210 * contains garbage. Must be called with the TODR lock held. 211 */ 212 void 213 todr_set_systime(time_t base) 214 { 215 bool badbase = false; 216 bool waszero = (base == 0); 217 bool goodtime = false; 218 bool badrtc = false; 219 struct timespec ts; 220 struct timeval tv; 221 222 KASSERT(todr_lock_owned()); 223 224 rnd_add_data(NULL, &base, sizeof(base), 0); 225 226 if (base < 5 * SECS_PER_COMMON_YEAR) { 227 struct clock_ymdhms basedate; 228 229 /* 230 * If base is 0, assume filesystem time is just unknown 231 * instead of preposterous. Don't bark. 232 */ 233 if (base != 0) 234 printf("WARNING: preposterous time in file system\n"); 235 /* not going to use it anyway, if the chip is readable */ 236 basedate.dt_year = 2010; 237 basedate.dt_mon = 1; 238 basedate.dt_day = 1; 239 basedate.dt_hour = 12; 240 basedate.dt_min = 0; 241 basedate.dt_sec = 0; 242 base = clock_ymdhms_to_secs(&basedate); 243 badbase = true; 244 } 245 246 /* 247 * Some ports need to be supplied base in order to fabricate a time_t. 248 */ 249 if (todr_handle) 250 todr_handle->todr_base_time = base; 251 252 memset(&tv, 0, sizeof(tv)); 253 254 if ((todr_handle == NULL) || 255 (todr_gettime(todr_handle, &tv) != 0) || 256 (tv.tv_sec < (PREPOSTEROUS_YEARS * SECS_PER_COMMON_YEAR))) { 257 258 if (todr_handle != NULL) 259 printf("WARNING: preposterous TOD clock time\n"); 260 else 261 printf("WARNING: no TOD clock present\n"); 262 badrtc = true; 263 } else { 264 time_t deltat = tv.tv_sec - base; 265 266 if (deltat < 0) 267 deltat = -deltat; 268 269 if (!badbase && deltat >= 2 * SECS_PER_DAY) { 270 271 if (tv.tv_sec < base) { 272 /* 273 * The clock should never go backwards 274 * relative to filesystem time. If it 275 * does by more than the threshold, 276 * believe the filesystem. 277 */ 278 printf("WARNING: clock lost %" PRId64 " days\n", 279 deltat / SECS_PER_DAY); 280 badrtc = true; 281 } else { 282 aprint_verbose("WARNING: clock gained %" PRId64 283 " days\n", deltat / SECS_PER_DAY); 284 goodtime = true; 285 } 286 } else { 287 goodtime = true; 288 } 289 290 rnd_add_data(NULL, &tv, sizeof(tv), 0); 291 } 292 293 /* if the rtc time is bad, use the filesystem time */ 294 if (badrtc) { 295 if (badbase) { 296 printf("WARNING: using default initial time\n"); 297 } else { 298 printf("WARNING: using filesystem time\n"); 299 } 300 tv.tv_sec = base; 301 tv.tv_usec = 0; 302 } 303 304 timeset = true; 305 306 ts.tv_sec = tv.tv_sec; 307 ts.tv_nsec = tv.tv_usec * 1000; 308 tc_setclock(&ts); 309 310 if (waszero || goodtime) 311 return; 312 313 printf("WARNING: CHECK AND RESET THE DATE!\n"); 314 } 315 316 /* 317 * todr_save_systime: 318 * Save the current system time back to the TOD clock. 319 * Must be called with the TODR lock held. 320 */ 321 void 322 todr_save_systime(void) 323 { 324 struct timeval tv; 325 326 KASSERT(todr_lock_owned()); 327 328 /* 329 * We might have been called by boot() due to a crash early 330 * on. Don't reset the clock chip if we don't know what time 331 * it is. 332 */ 333 if (!timeset) 334 return; 335 336 getmicrotime(&tv); 337 338 if (tv.tv_sec == 0) 339 return; 340 341 if (todr_handle) 342 if (todr_settime(todr_handle, &tv) != 0) 343 printf("Cannot set TOD clock time\n"); 344 } 345 346 /* 347 * inittodr: 348 * Legacy wrapper around todr_set_systime(). 349 */ 350 void 351 inittodr(time_t base) 352 { 353 354 todr_lock(); 355 todr_set_systime(base); 356 todr_unlock(); 357 } 358 359 /* 360 * resettodr: 361 * Legacy wrapper around todr_save_systime(). 362 */ 363 void 364 resettodr(void) 365 { 366 367 /* 368 * If we're shutting down, we don't want to get stuck in case 369 * someone was already fiddling with the TOD clock. 370 */ 371 if (shutting_down) { 372 if (mutex_tryenter(&todr_mutex) == 0) { 373 printf("WARNING: Cannot set TOD clock time (busy)\n"); 374 return; 375 } 376 } else { 377 todr_lock(); 378 } 379 todr_save_systime(); 380 todr_unlock(); 381 } 382 383 #ifdef TODR_DEBUG 384 static void 385 todr_debug(const char *prefix, int rv, struct clock_ymdhms *dt, 386 struct timeval *tvp) 387 { 388 struct timeval tv_val; 389 struct clock_ymdhms dt_val; 390 391 if (dt == NULL) { 392 clock_secs_to_ymdhms(tvp->tv_sec, &dt_val); 393 dt = &dt_val; 394 } 395 if (tvp == NULL) { 396 tvp = &tv_val; 397 tvp->tv_sec = clock_ymdhms_to_secs(dt); 398 tvp->tv_usec = 0; 399 } 400 printf("%s: rv = %d\n", prefix, rv); 401 printf("%s: rtc_offset = %d\n", prefix, rtc_offset); 402 printf("%s: %4u/%02u/%02u %02u:%02u:%02u, (wday %d) (epoch %u.%06u)\n", 403 prefix, 404 (unsigned)dt->dt_year, dt->dt_mon, dt->dt_day, 405 dt->dt_hour, dt->dt_min, dt->dt_sec, 406 dt->dt_wday, (unsigned)tvp->tv_sec, (unsigned)tvp->tv_usec); 407 } 408 #else /* !TODR_DEBUG */ 409 #define todr_debug(prefix, rv, dt, tvp) 410 #endif /* TODR_DEBUG */ 411 412 static int 413 todr_wenable(todr_chip_handle_t todr, int onoff) 414 { 415 416 if (todr->todr_setwen != NULL) 417 return todr->todr_setwen(todr, onoff); 418 419 return 0; 420 } 421 422 #define ENABLE_TODR_WRITES() \ 423 do { \ 424 if ((rv = todr_wenable(tch, 1)) != 0) { \ 425 printf("%s: cannot enable TODR writes\n", __func__); \ 426 return rv; \ 427 } \ 428 } while (/*CONSTCOND*/0) 429 430 #define DISABLE_TODR_WRITES() \ 431 do { \ 432 if (todr_wenable(tch, 0) != 0) \ 433 printf("%s: WARNING: cannot disable TODR writes\n", \ 434 __func__); \ 435 } while (/*CONSTCOND*/0) 436 437 static int 438 todr_gettime(todr_chip_handle_t tch, struct timeval *tvp) 439 { 440 int rv; 441 442 /* 443 * Write-enable is used even when reading the TODR because 444 * writing to registers may be required in order to do so. 445 */ 446 447 if (tch->todr_gettime) { 448 ENABLE_TODR_WRITES(); 449 rv = tch->todr_gettime(tch, tvp); 450 DISABLE_TODR_WRITES(); 451 /* 452 * Some unconverted ports have their own references to 453 * rtc_offset. A converted port must not do that. 454 */ 455 if (rv == 0) 456 tvp->tv_sec += rtc_offset * 60; 457 todr_debug("TODR-GET-SECS", rv, NULL, tvp); 458 return rv; 459 } else if (tch->todr_gettime_ymdhms) { 460 struct clock_ymdhms dt = { 0 }; 461 ENABLE_TODR_WRITES(); 462 rv = tch->todr_gettime_ymdhms(tch, &dt); 463 DISABLE_TODR_WRITES(); 464 todr_debug("TODR-GET-YMDHMS", rv, &dt, NULL); 465 if (rv) 466 return rv; 467 468 /* 469 * Simple sanity checks. Note that this includes a 470 * value for clocks that can return a leap second. 471 * Note that we don't support double leap seconds, 472 * since this was apparently an error/misunderstanding 473 * on the part of the ISO C committee, and can never 474 * actually occur. If your clock issues us a double 475 * leap second, it must be broken. Ultimately, you'd 476 * have to be trying to read time at precisely that 477 * instant to even notice, so even broken clocks will 478 * work the vast majority of the time. In such a case 479 * it is recommended correction be applied in the 480 * clock driver. 481 */ 482 if (dt.dt_mon < 1 || dt.dt_mon > 12 || 483 dt.dt_day < 1 || dt.dt_day > 31 || 484 dt.dt_hour > 23 || dt.dt_min > 59 || dt.dt_sec > 60) { 485 return SET_ERROR(EINVAL); 486 } 487 tvp->tv_sec = clock_ymdhms_to_secs(&dt) + rtc_offset * 60; 488 tvp->tv_usec = 0; 489 return tvp->tv_sec < 0 ? SET_ERROR(EINVAL) : 0; 490 } 491 492 return SET_ERROR(ENXIO); 493 } 494 495 static int 496 todr_settime(todr_chip_handle_t tch, struct timeval *tvp) 497 { 498 int rv; 499 500 if (tch->todr_settime) { 501 struct timeval copy = *tvp; 502 copy.tv_sec -= rtc_offset * 60; 503 ENABLE_TODR_WRITES(); 504 rv = tch->todr_settime(tch, ©); 505 DISABLE_TODR_WRITES(); 506 todr_debug("TODR-SET-SECS", rv, NULL, tvp); 507 return rv; 508 } else if (tch->todr_settime_ymdhms) { 509 struct clock_ymdhms dt; 510 time_t sec = tvp->tv_sec - rtc_offset * 60; 511 if (tvp->tv_usec >= 500000) 512 sec++; 513 clock_secs_to_ymdhms(sec, &dt); 514 ENABLE_TODR_WRITES(); 515 rv = tch->todr_settime_ymdhms(tch, &dt); 516 DISABLE_TODR_WRITES(); 517 todr_debug("TODR-SET-YMDHMS", rv, &dt, NULL); 518 return rv; 519 } 520 521 return SET_ERROR(ENXIO); 522 } 523