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 static int 208 wd_ioctl_patpat(caddr_t data) 209 { 210 u_int u; 211 212 u = *(u_int *)data; 213 if (u & ~(WD_ACTIVE | WD_PASSIVE | WD_LASTVAL | WD_INTERVAL)) 214 return (EINVAL); 215 if ((u & (WD_ACTIVE | WD_PASSIVE)) == (WD_ACTIVE | WD_PASSIVE)) 216 return (EINVAL); 217 if ((u & (WD_ACTIVE | WD_PASSIVE)) == 0 && ((u & WD_INTERVAL) > 0 || 218 (u & WD_LASTVAL) != 0)) 219 return (EINVAL); 220 if (u & WD_PASSIVE) 221 return (ENOSYS); /* XXX Not implemented yet */ 222 u &= ~(WD_ACTIVE | WD_PASSIVE); 223 224 return (wdog_kern_pat(u)); 225 } 226 227 static int 228 wd_get_time_left(struct thread *td, time_t *remainp) 229 { 230 struct timespec ts; 231 int error; 232 233 error = kern_clock_gettime(td, CLOCK_MONOTONIC_FAST, &ts); 234 if (error) 235 return (error); 236 if (!wd_lastpat_valid) 237 return (ENOENT); 238 *remainp = ts.tv_sec - wd_lastpat; 239 return (0); 240 } 241 242 static void 243 wd_timeout_cb(void *arg) 244 { 245 const char *type = arg; 246 247 #ifdef DDB 248 if ((wd_pretimeout_act & WD_SOFT_DDB)) { 249 char kdb_why[80]; 250 snprintf(kdb_why, sizeof(kdb_why), "watchdog %s-timeout", type); 251 kdb_backtrace(); 252 kdb_enter(KDB_WHY_WATCHDOG, kdb_why); 253 } 254 #endif 255 if ((wd_pretimeout_act & WD_SOFT_LOG)) 256 log(LOG_EMERG, "watchdog %s-timeout, WD_SOFT_LOG\n", type); 257 if ((wd_pretimeout_act & WD_SOFT_PRINTF)) 258 printf("watchdog %s-timeout, WD_SOFT_PRINTF\n", type); 259 if ((wd_pretimeout_act & WD_SOFT_PANIC)) 260 panic("watchdog %s-timeout, WD_SOFT_PANIC set", type); 261 } 262 263 /* 264 * Called to manage timeouts. 265 * newtimeout needs to be in the range of 0 to actual watchdog timeout. 266 * if 0, we disable the pre-timeout. 267 * otherwise we set the pre-timeout provided it's not greater than the 268 * current actual watchdog timeout. 269 */ 270 static int 271 wd_set_pretimeout(sbintime_t newtimeout, int disableiftoolong) 272 { 273 sbintime_t utime; 274 sbintime_t timeout_left; 275 276 utime = wdog_kern_last_timeout_sbt(); 277 /* do not permit a pre-timeout >= than the timeout. */ 278 if (newtimeout >= utime) { 279 /* 280 * If 'disableiftoolong' then just fall through 281 * so as to disable the pre-watchdog 282 */ 283 if (disableiftoolong) 284 newtimeout = 0; 285 else 286 return EINVAL; 287 } 288 289 /* disable the pre-timeout */ 290 if (newtimeout == 0) { 291 wd_pretimeout = 0; 292 callout_stop(&wd_pretimeo_handle); 293 return 0; 294 } 295 296 timeout_left = utime - newtimeout; 297 #if 0 298 printf("wd_set_pretimeout: " 299 "newtimeout: %d, " 300 "utime: %d -> utime_ticks: %d, " 301 "hz*newtimeout: %d, " 302 "timeout_ticks: %d -> sec: %d\n", 303 newtimeout, 304 utime, pow2ns_to_ticks(utime), 305 hz*newtimeout, 306 timeout_ticks, timeout_ticks / hz); 307 #endif 308 309 /* We determined the value is sane, so reset the callout */ 310 (void) callout_reset_sbt(&wd_pretimeo_handle, 311 timeout_left, 0, wd_timeout_cb, "pre", 0); 312 wd_pretimeout = newtimeout; 313 return 0; 314 } 315 316 static int 317 wd_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t data, 318 int flags __unused, struct thread *td) 319 { 320 sbintime_t sb; 321 u_int u; 322 time_t timeleft; 323 int error; 324 325 error = 0; 326 327 switch (cmd) { 328 case WDIOC_SETSOFT: 329 u = *(int *)data; 330 /* do nothing? */ 331 if (u == wd_softtimer) 332 break; 333 /* If there is a pending timeout disallow this ioctl */ 334 if (wd_last_u != 0) { 335 error = EINVAL; 336 break; 337 } 338 wd_softtimer = u; 339 break; 340 case WDIOC_SETSOFTTIMEOUTACT: 341 u = *(int *)data; 342 if (wd_valid_act(u)) { 343 wd_softtimeout_act = u; 344 } else { 345 error = EINVAL; 346 } 347 break; 348 case WDIOC_SETPRETIMEOUTACT: 349 u = *(int *)data; 350 if (wd_valid_act(u)) { 351 wd_pretimeout_act = u; 352 } else { 353 error = EINVAL; 354 } 355 break; 356 #ifdef COMPAT_FREEBSD14 357 case WDIOC_GETPRETIMEOUT_14: 358 *(int *)data = (int)(wd_pretimeout / SBT_1S); 359 break; 360 case WDIOC_SETPRETIMEOUT_14: 361 error = wd_set_pretimeout(*(int *)data * SBT_1S, false); 362 break; 363 case WDIOC_GETTIMELEFT_14: 364 error = wd_get_time_left(td, &timeleft); 365 if (error) 366 break; 367 *(int *)data = (int)timeleft; 368 break; 369 case WDIOC_SETTIMEOUT_14: 370 u = *(u_int *)data; 371 error = wdog_kern_pat_sbt(mstosbt(u * 1000ULL)); 372 break; 373 case WDIOC_GETTIMEOUT_14: 374 u = wdog_kern_last_timeout(); 375 *(u_int *)data = u; 376 break; 377 case WDIOCPATPAT_14: 378 error = wd_ioctl_patpat(data); 379 break; 380 #endif 381 382 /* New API */ 383 case WDIOC_CONTROL: 384 wdog_control(*(int *)data); 385 break; 386 case WDIOC_SETTIMEOUT: 387 sb = *(sbintime_t *)data; 388 error = wdog_kern_pat_sbt(sb); 389 break; 390 case WDIOC_GETTIMEOUT: 391 *(sbintime_t *)data = wdog_kern_last_timeout_sbt(); 392 break; 393 case WDIOC_GETTIMELEFT: 394 error = wd_get_time_left(td, &timeleft); 395 if (error) 396 break; 397 *(sbintime_t *)data = (sbintime_t)timeleft * SBT_1S; 398 break; 399 case WDIOC_GETPRETIMEOUT: 400 *(sbintime_t *)data = wd_pretimeout; 401 break; 402 case WDIOC_SETPRETIMEOUT: 403 error = wd_set_pretimeout(*(sbintime_t *)data, false); 404 break; 405 default: 406 error = ENOIOCTL; 407 break; 408 } 409 return (error); 410 } 411 412 /* 413 * Return the last timeout set, this is NOT the seconds from NOW until timeout, 414 * rather it is the amount of seconds passed to WDIOCPATPAT/WDIOC_SETTIMEOUT. 415 */ 416 u_int 417 wdog_kern_last_timeout(void) 418 { 419 420 return (wd_last_u); 421 } 422 423 sbintime_t 424 wdog_kern_last_timeout_sbt(void) 425 { 426 return (wd_last_sbt); 427 } 428 429 static struct cdevsw wd_cdevsw = { 430 .d_version = D_VERSION, 431 .d_ioctl = wd_ioctl, 432 .d_name = "watchdog", 433 }; 434 435 static int 436 watchdog_modevent(module_t mod __unused, int type, void *data __unused) 437 { 438 switch(type) { 439 case MOD_LOAD: 440 callout_init(&wd_pretimeo_handle, 1); 441 callout_init(&wd_softtimeo_handle, 1); 442 wd_dev = make_dev(&wd_cdevsw, 0, 443 UID_ROOT, GID_WHEEL, 0600, _PATH_WATCHDOG); 444 return 0; 445 case MOD_UNLOAD: 446 callout_stop(&wd_pretimeo_handle); 447 callout_stop(&wd_softtimeo_handle); 448 callout_drain(&wd_pretimeo_handle); 449 callout_drain(&wd_softtimeo_handle); 450 destroy_dev(wd_dev); 451 return 0; 452 case MOD_SHUTDOWN: 453 return 0; 454 default: 455 return EOPNOTSUPP; 456 } 457 } 458 459 DEV_MODULE(watchdog, watchdog_modevent, NULL); 460