1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2004 Poul-Henning Kamp 5 * Copyright (c) 2013 iXsystems.com, 6 * author: Alfred Perlstein <alfred@freebsd.org> 7 * 8 * All rights reserved. 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 * in this position and unchanged. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 23 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 * 31 */ 32 33 #include "opt_ddb.h" 34 35 #include <sys/param.h> 36 #include <sys/bus.h> 37 #include <sys/conf.h> 38 #include <sys/eventhandler.h> 39 #include <sys/kdb.h> 40 #include <sys/kernel.h> 41 #include <sys/malloc.h> 42 #include <sys/module.h> 43 #include <sys/mutex.h> 44 #include <sys/sysctl.h> 45 #include <sys/syslog.h> 46 #include <sys/systm.h> 47 #include <sys/uio.h> 48 #include <sys/watchdog.h> 49 #include <machine/bus.h> 50 51 #include <sys/syscallsubr.h> /* kern_clock_gettime() */ 52 53 #ifdef COMPAT_FREEBSD14 54 #define WDIOCPATPAT_14 _IOW('W', 42, u_int) /* pat the watchdog */ 55 #define WDIOC_SETTIMEOUT_14 _IOW('W', 43, int) /* set/reset the timer */ 56 #define WDIOC_GETTIMEOUT_14 _IOR('W', 44, int) /* get total timeout */ 57 #define WDIOC_GETTIMELEFT_14 _IOR('W', 45, int) /* get time left */ 58 #define WDIOC_GETPRETIMEOUT_14 _IOR('W', 46, int) /* get the pre-timeout */ 59 #define WDIOC_SETPRETIMEOUT_14 _IOW('W', 47, int) /* set the pre-timeout */ 60 #endif 61 62 static int wd_set_pretimeout(sbintime_t newtimeout, int disableiftoolong); 63 static void wd_timeout_cb(void *arg); 64 65 static struct callout wd_pretimeo_handle; 66 static sbintime_t wd_pretimeout; 67 static int wd_pretimeout_act = WD_SOFT_LOG; 68 69 static struct callout wd_softtimeo_handle; 70 static int wd_softtimer; /* true = use softtimer instead of hardware 71 watchdog */ 72 static int wd_softtimeout_act = WD_SOFT_LOG; /* action for the software timeout */ 73 74 static struct cdev *wd_dev; 75 static volatile sbintime_t wd_last_sbt; /* last timeout value (sbt) */ 76 static sbintime_t wd_last_sbt_sysctl; /* last timeout value (sbt) */ 77 static volatile u_int wd_last_u; /* last timeout value set by kern_do_pat */ 78 static u_int wd_last_u_sysctl; /* last timeout value set by kern_do_pat */ 79 static u_int wd_last_u_sysctl_secs; /* wd_last_u in seconds */ 80 81 SYSCTL_NODE(_hw, OID_AUTO, watchdog, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, 82 "Main watchdog device"); 83 SYSCTL_UINT(_hw_watchdog, OID_AUTO, wd_last_u, CTLFLAG_RD, 84 &wd_last_u_sysctl, 0, "Watchdog last update time"); 85 SYSCTL_UINT(_hw_watchdog, OID_AUTO, wd_last_u_secs, CTLFLAG_RD, 86 &wd_last_u_sysctl_secs, 0, "Watchdog last update time"); 87 SYSCTL_SBINTIME_MSEC(_hw_watchdog, OID_AUTO, wd_last_msecs, CTLFLAG_RD, 88 &wd_last_sbt_sysctl, "Watchdog last update time (milliseconds)"); 89 90 static int wd_lastpat_valid = 0; 91 static time_t wd_lastpat = 0; /* when the watchdog was last patted */ 92 93 /* Hook for external software watchdog to register for use if needed */ 94 void (*wdog_software_attach)(void); 95 96 /* Legacy interface to watchdog. */ 97 int 98 wdog_kern_pat(u_int utim) 99 { 100 sbintime_t sbt; 101 102 if ((utim & WD_LASTVAL) != 0 && (utim & WD_INTERVAL) > 0) 103 return (EINVAL); 104 105 if ((utim & WD_LASTVAL) != 0) { 106 return (wdog_control(WD_CTRL_RESET)); 107 } 108 109 utim &= WD_INTERVAL; 110 if (utim == WD_TO_NEVER) 111 sbt = 0; 112 else 113 sbt = nstosbt(1 << utim); 114 115 return (wdog_kern_pat_sbt(sbt)); 116 } 117 118 int 119 wdog_control(int ctrl) 120 { 121 /* Disable takes precedence */ 122 if (ctrl == WD_CTRL_DISABLE) { 123 wdog_kern_pat(0); 124 } 125 126 if ((ctrl & WD_CTRL_RESET) != 0) { 127 wdog_kern_pat_sbt(wd_last_sbt); 128 } else if ((ctrl & WD_CTRL_ENABLE) != 0) { 129 wdog_kern_pat_sbt(wd_last_sbt); 130 } 131 132 return (0); 133 } 134 135 int 136 wdog_kern_pat_sbt(sbintime_t sbt) 137 { 138 sbintime_t error_sbt = 0; 139 int pow2ns = 0; 140 int error = 0; 141 static bool first = true; 142 143 /* legacy uses power-of-2-nanoseconds time. */ 144 if (sbt != 0) { 145 pow2ns = flsl(sbttons(sbt)); 146 } 147 if (wd_last_sbt != sbt) { 148 wd_last_u = pow2ns; 149 wd_last_u_sysctl = wd_last_u; 150 wd_last_u_sysctl_secs = sbt / SBT_1S; 151 152 wd_last_sbt = sbt; 153 } 154 155 if (sbt != 0) 156 error = EOPNOTSUPP; 157 158 if (wd_softtimer) { 159 if (sbt == 0) { 160 callout_stop(&wd_softtimeo_handle); 161 } else { 162 (void) callout_reset_sbt(&wd_softtimeo_handle, 163 sbt, 0, wd_timeout_cb, "soft", 0); 164 } 165 error = 0; 166 } else { 167 EVENTHANDLER_INVOKE(watchdog_sbt_list, sbt, &error_sbt, &error); 168 EVENTHANDLER_INVOKE(watchdog_list, pow2ns, &error); 169 } 170 /* 171 * If no hardware watchdog responded, we have not tried to 172 * attach an external software watchdog, and one is available, 173 * attach it now and retry. 174 */ 175 if (error == EOPNOTSUPP && first && wdog_software_attach != NULL) { 176 (*wdog_software_attach)(); 177 EVENTHANDLER_INVOKE(watchdog_sbt_list, sbt, &error_sbt, &error); 178 EVENTHANDLER_INVOKE(watchdog_list, pow2ns, &error); 179 } 180 first = false; 181 182 /* TODO: Print a (rate limited?) warning if error_sbt is too far away */ 183 wd_set_pretimeout(wd_pretimeout, true); 184 if (!error) { 185 struct timespec ts; 186 187 error = kern_clock_gettime(curthread /* XXX */, 188 CLOCK_MONOTONIC_FAST, &ts); 189 if (!error) { 190 wd_lastpat = ts.tv_sec; 191 wd_lastpat_valid = 1; 192 } 193 } 194 195 return (error); 196 } 197 198 static int 199 wd_valid_act(int act) 200 { 201 202 if ((act & ~(WD_SOFT_MASK)) != 0) 203 return false; 204 return true; 205 } 206 207 #ifdef COMPAT_FREEBSD14 208 static int 209 wd_ioctl_patpat(caddr_t data) 210 { 211 u_int u; 212 213 u = *(u_int *)data; 214 if (u & ~(WD_ACTIVE | WD_PASSIVE | WD_LASTVAL | WD_INTERVAL)) 215 return (EINVAL); 216 if ((u & (WD_ACTIVE | WD_PASSIVE)) == (WD_ACTIVE | WD_PASSIVE)) 217 return (EINVAL); 218 if ((u & (WD_ACTIVE | WD_PASSIVE)) == 0 && ((u & WD_INTERVAL) > 0 || 219 (u & WD_LASTVAL) != 0)) 220 return (EINVAL); 221 if (u & WD_PASSIVE) 222 return (ENOSYS); /* XXX Not implemented yet */ 223 u &= ~(WD_ACTIVE | WD_PASSIVE); 224 225 return (wdog_kern_pat(u)); 226 } 227 #endif 228 229 static int 230 wd_get_time_left(struct thread *td, time_t *remainp) 231 { 232 struct timespec ts; 233 int error; 234 235 error = kern_clock_gettime(td, CLOCK_MONOTONIC_FAST, &ts); 236 if (error) 237 return (error); 238 if (!wd_lastpat_valid) 239 return (ENOENT); 240 *remainp = ts.tv_sec - wd_lastpat; 241 return (0); 242 } 243 244 static void 245 wd_timeout_cb(void *arg) 246 { 247 const char *type = arg; 248 249 #ifdef DDB 250 if ((wd_pretimeout_act & WD_SOFT_DDB)) { 251 char kdb_why[80]; 252 snprintf(kdb_why, sizeof(kdb_why), "watchdog %s-timeout", type); 253 kdb_backtrace(); 254 kdb_enter(KDB_WHY_WATCHDOG, kdb_why); 255 } 256 #endif 257 if ((wd_pretimeout_act & WD_SOFT_LOG)) 258 log(LOG_EMERG, "watchdog %s-timeout, WD_SOFT_LOG\n", type); 259 if ((wd_pretimeout_act & WD_SOFT_PRINTF)) 260 printf("watchdog %s-timeout, WD_SOFT_PRINTF\n", type); 261 if ((wd_pretimeout_act & WD_SOFT_PANIC)) 262 panic("watchdog %s-timeout, WD_SOFT_PANIC set", type); 263 } 264 265 /* 266 * Called to manage timeouts. 267 * newtimeout needs to be in the range of 0 to actual watchdog timeout. 268 * if 0, we disable the pre-timeout. 269 * otherwise we set the pre-timeout provided it's not greater than the 270 * current actual watchdog timeout. 271 */ 272 static int 273 wd_set_pretimeout(sbintime_t newtimeout, int disableiftoolong) 274 { 275 sbintime_t utime; 276 sbintime_t timeout_left; 277 278 utime = wdog_kern_last_timeout_sbt(); 279 /* do not permit a pre-timeout >= than the timeout. */ 280 if (newtimeout >= utime) { 281 /* 282 * If 'disableiftoolong' then just fall through 283 * so as to disable the pre-watchdog 284 */ 285 if (disableiftoolong) 286 newtimeout = 0; 287 else 288 return EINVAL; 289 } 290 291 /* disable the pre-timeout */ 292 if (newtimeout == 0) { 293 wd_pretimeout = 0; 294 callout_stop(&wd_pretimeo_handle); 295 return 0; 296 } 297 298 timeout_left = utime - newtimeout; 299 #if 0 300 printf("wd_set_pretimeout: " 301 "newtimeout: %d, " 302 "utime: %d -> utime_ticks: %d, " 303 "hz*newtimeout: %d, " 304 "timeout_ticks: %d -> sec: %d\n", 305 newtimeout, 306 utime, pow2ns_to_ticks(utime), 307 hz*newtimeout, 308 timeout_ticks, timeout_ticks / hz); 309 #endif 310 311 /* We determined the value is sane, so reset the callout */ 312 (void) callout_reset_sbt(&wd_pretimeo_handle, 313 timeout_left, 0, wd_timeout_cb, "pre", 0); 314 wd_pretimeout = newtimeout; 315 return 0; 316 } 317 318 static int 319 wd_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t data, 320 int flags __unused, struct thread *td) 321 { 322 sbintime_t sb; 323 u_int u; 324 time_t timeleft; 325 int error; 326 327 error = 0; 328 329 switch (cmd) { 330 case WDIOC_SETSOFT: 331 u = *(int *)data; 332 /* do nothing? */ 333 if (u == wd_softtimer) 334 break; 335 /* If there is a pending timeout disallow this ioctl */ 336 if (wd_last_u != 0) { 337 error = EINVAL; 338 break; 339 } 340 wd_softtimer = u; 341 break; 342 case WDIOC_SETSOFTTIMEOUTACT: 343 u = *(int *)data; 344 if (wd_valid_act(u)) { 345 wd_softtimeout_act = u; 346 } else { 347 error = EINVAL; 348 } 349 break; 350 case WDIOC_SETPRETIMEOUTACT: 351 u = *(int *)data; 352 if (wd_valid_act(u)) { 353 wd_pretimeout_act = u; 354 } else { 355 error = EINVAL; 356 } 357 break; 358 #ifdef COMPAT_FREEBSD14 359 case WDIOC_GETPRETIMEOUT_14: 360 *(int *)data = (int)(wd_pretimeout / SBT_1S); 361 break; 362 case WDIOC_SETPRETIMEOUT_14: 363 error = wd_set_pretimeout(*(int *)data * SBT_1S, false); 364 break; 365 case WDIOC_GETTIMELEFT_14: 366 error = wd_get_time_left(td, &timeleft); 367 if (error) 368 break; 369 *(int *)data = (int)timeleft; 370 break; 371 case WDIOC_SETTIMEOUT_14: 372 u = *(u_int *)data; 373 error = wdog_kern_pat_sbt(mstosbt(u * 1000ULL)); 374 break; 375 case WDIOC_GETTIMEOUT_14: 376 u = wdog_kern_last_timeout(); 377 *(u_int *)data = u; 378 break; 379 case WDIOCPATPAT_14: 380 error = wd_ioctl_patpat(data); 381 break; 382 #endif 383 384 /* New API */ 385 case WDIOC_CONTROL: 386 wdog_control(*(int *)data); 387 break; 388 case WDIOC_SETTIMEOUT: 389 sb = *(sbintime_t *)data; 390 error = wdog_kern_pat_sbt(sb); 391 break; 392 case WDIOC_GETTIMEOUT: 393 *(sbintime_t *)data = wdog_kern_last_timeout_sbt(); 394 break; 395 case WDIOC_GETTIMELEFT: 396 error = wd_get_time_left(td, &timeleft); 397 if (error) 398 break; 399 *(sbintime_t *)data = (sbintime_t)timeleft * SBT_1S; 400 break; 401 case WDIOC_GETPRETIMEOUT: 402 *(sbintime_t *)data = wd_pretimeout; 403 break; 404 case WDIOC_SETPRETIMEOUT: 405 error = wd_set_pretimeout(*(sbintime_t *)data, false); 406 break; 407 default: 408 error = ENOIOCTL; 409 break; 410 } 411 return (error); 412 } 413 414 /* 415 * Return the last timeout set, this is NOT the seconds from NOW until timeout, 416 * rather it is the amount of seconds passed to WDIOCPATPAT/WDIOC_SETTIMEOUT. 417 */ 418 u_int 419 wdog_kern_last_timeout(void) 420 { 421 422 return (wd_last_u); 423 } 424 425 sbintime_t 426 wdog_kern_last_timeout_sbt(void) 427 { 428 return (wd_last_sbt); 429 } 430 431 static struct cdevsw wd_cdevsw = { 432 .d_version = D_VERSION, 433 .d_ioctl = wd_ioctl, 434 .d_name = "watchdog", 435 }; 436 437 static int 438 watchdog_modevent(module_t mod __unused, int type, void *data __unused) 439 { 440 switch(type) { 441 case MOD_LOAD: 442 callout_init(&wd_pretimeo_handle, 1); 443 callout_init(&wd_softtimeo_handle, 1); 444 wd_dev = make_dev(&wd_cdevsw, 0, 445 UID_ROOT, GID_WHEEL, 0600, _PATH_WATCHDOG); 446 return 0; 447 case MOD_UNLOAD: 448 callout_stop(&wd_pretimeo_handle); 449 callout_stop(&wd_softtimeo_handle); 450 callout_drain(&wd_pretimeo_handle); 451 callout_drain(&wd_softtimeo_handle); 452 destroy_dev(wd_dev); 453 return 0; 454 case MOD_SHUTDOWN: 455 return 0; 456 default: 457 return EOPNOTSUPP; 458 } 459 } 460 461 DEV_MODULE(watchdog, watchdog_modevent, NULL); 462