1 1.24 rin /* $NetBSD: swwdog.c,v 1.24 2024/11/07 09:47:40 rin Exp $ */ 2 1.1 smb 3 1.1 smb /* 4 1.1 smb * Copyright (c) 2004, 2005 Steven M. Bellovin 5 1.1 smb * All rights reserved. 6 1.1 smb * 7 1.1 smb * Redistribution and use in source and binary forms, with or without 8 1.1 smb * modification, are permitted provided that the following conditions 9 1.1 smb * are met: 10 1.1 smb * 1. Redistributions of source code must retain the above copyright 11 1.1 smb * notice, this list of conditions and the following disclaimer. 12 1.1 smb * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 smb * notice, this list of conditions and the following disclaimer in the 14 1.1 smb * documentation and/or other materials provided with the distribution. 15 1.1 smb * 3. All advertising materials mentioning features or use of this software 16 1.1 smb * must display the following acknowledgement: 17 1.1 smb * This product includes software developed by Steven M. Bellovin 18 1.1 smb * 4. The name of the author contributors may not be used to endorse or 19 1.1 smb * promote products derived from this software without specific prior 20 1.1 smb * written permission. 21 1.1 smb * 22 1.1 smb * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS 23 1.1 smb * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 24 1.1 smb * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 25 1.1 smb * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS 26 1.1 smb * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 27 1.1 smb * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 28 1.1 smb * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 29 1.1 smb * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 30 1.1 smb * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31 1.1 smb * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 1.1 smb * POSSIBILITY OF SUCH DAMAGE. 33 1.1 smb */ 34 1.1 smb 35 1.1 smb #include <sys/cdefs.h> 36 1.24 rin __KERNEL_RCSID(0, "$NetBSD: swwdog.c,v 1.24 2024/11/07 09:47:40 rin Exp $"); 37 1.1 smb 38 1.1 smb /* 39 1.1 smb * 40 1.1 smb * Software watchdog timer 41 1.1 smb * 42 1.1 smb */ 43 1.1 smb #include <sys/param.h> 44 1.14 pgoyette #include <sys/device.h> 45 1.2 perry #include <sys/callout.h> 46 1.2 perry #include <sys/kernel.h> 47 1.10 pgoyette #include <sys/kmem.h> 48 1.1 smb #include <sys/reboot.h> 49 1.1 smb #include <sys/systm.h> 50 1.10 pgoyette #include <sys/sysctl.h> 51 1.1 smb #include <sys/wdog.h> 52 1.12 pooka #include <sys/workqueue.h> 53 1.14 pgoyette #include <sys/module.h> 54 1.1 smb #include <dev/sysmon/sysmonvar.h> 55 1.1 smb 56 1.14 pgoyette #ifndef _MODULE 57 1.14 pgoyette #include "opt_modular.h" 58 1.14 pgoyette #endif 59 1.10 pgoyette 60 1.1 smb struct swwdog_softc { 61 1.14 pgoyette device_t sc_dev; 62 1.14 pgoyette struct sysmon_wdog sc_smw; 63 1.14 pgoyette struct callout sc_c; 64 1.14 pgoyette int sc_armed; 65 1.10 pgoyette }; 66 1.10 pgoyette 67 1.14 pgoyette bool swwdog_reboot = false; /* false --> panic , true --> reboot */ 68 1.14 pgoyette 69 1.14 pgoyette static struct workqueue *wq; 70 1.14 pgoyette static device_t swwdog_dev; 71 1.14 pgoyette 72 1.16 pgoyette MODULE(MODULE_CLASS_DRIVER, swwdog, "sysmon_wdog"); 73 1.14 pgoyette 74 1.17 christos #ifdef _MODULE 75 1.14 pgoyette CFDRIVER_DECL(swwdog, DV_DULL, NULL); 76 1.17 christos #endif 77 1.14 pgoyette 78 1.14 pgoyette int swwdogattach(int); 79 1.14 pgoyette 80 1.14 pgoyette static int swwdog_setmode(struct sysmon_wdog *); 81 1.14 pgoyette static int swwdog_tickle(struct sysmon_wdog *); 82 1.14 pgoyette static bool swwdog_suspend(device_t, const pmf_qual_t *); 83 1.14 pgoyette static int swwdog_arm(struct swwdog_softc *); 84 1.14 pgoyette static int swwdog_disarm(struct swwdog_softc *); 85 1.14 pgoyette 86 1.14 pgoyette static void swwdog_panic(void *); 87 1.14 pgoyette 88 1.10 pgoyette static int swwdog_match(device_t, cfdata_t, void *); 89 1.10 pgoyette static void swwdog_attach(device_t, device_t, void *); 90 1.10 pgoyette static int swwdog_detach(device_t, int); 91 1.10 pgoyette static bool swwdog_suspend(device_t, const pmf_qual_t *); 92 1.1 smb 93 1.14 pgoyette static int swwdog_init(void *); 94 1.14 pgoyette static int swwdog_fini(void *); 95 1.14 pgoyette static int swwdog_modcmd(modcmd_t, void *); 96 1.1 smb 97 1.10 pgoyette CFATTACH_DECL_NEW(swwdog, sizeof(struct swwdog_softc), 98 1.10 pgoyette swwdog_match, swwdog_attach, swwdog_detach, NULL); 99 1.14 pgoyette extern struct cfdriver swwdog_cd; 100 1.10 pgoyette 101 1.14 pgoyette #define SWDOG_DEFAULT 60 /* 60-second default period */ 102 1.11 pooka 103 1.12 pooka static void 104 1.12 pooka doreboot(struct work *wrkwrkwrk, void *p) 105 1.12 pooka { 106 1.12 pooka 107 1.21 thorpej kern_reboot(0, NULL); 108 1.12 pooka } 109 1.12 pooka 110 1.14 pgoyette int 111 1.10 pgoyette swwdogattach(int n __unused) 112 1.1 smb { 113 1.14 pgoyette int error; 114 1.10 pgoyette static struct cfdata cf; 115 1.10 pgoyette 116 1.12 pooka if (workqueue_create(&wq, "swwreboot", doreboot, NULL, 117 1.12 pooka PRI_NONE, IPL_NONE, 0) != 0) { 118 1.12 pooka aprint_error("failed to create swwdog reboot wq"); 119 1.14 pgoyette return 1; 120 1.12 pooka } 121 1.12 pooka 122 1.14 pgoyette error = config_cfattach_attach(swwdog_cd.cd_name, &swwdog_ca); 123 1.14 pgoyette if (error) { 124 1.14 pgoyette aprint_error("%s: unable to attach cfattach\n", 125 1.14 pgoyette swwdog_cd.cd_name); 126 1.12 pooka workqueue_destroy(wq); 127 1.14 pgoyette return error; 128 1.10 pgoyette } 129 1.1 smb 130 1.10 pgoyette cf.cf_name = swwdog_cd.cd_name; 131 1.10 pgoyette cf.cf_atname = swwdog_cd.cd_name; 132 1.10 pgoyette cf.cf_unit = 0; 133 1.10 pgoyette cf.cf_fstate = FSTATE_STAR; 134 1.14 pgoyette cf.cf_pspec = NULL; 135 1.14 pgoyette cf.cf_loc = NULL; 136 1.14 pgoyette cf.cf_flags = 0; 137 1.14 pgoyette 138 1.14 pgoyette swwdog_dev = config_attach_pseudo(&cf); 139 1.14 pgoyette 140 1.14 pgoyette if (swwdog_dev == NULL) { 141 1.14 pgoyette config_cfattach_detach(swwdog_cd.cd_name, &swwdog_ca); 142 1.14 pgoyette workqueue_destroy(wq); 143 1.14 pgoyette return 1; 144 1.14 pgoyette } 145 1.14 pgoyette return 0; 146 1.10 pgoyette } 147 1.10 pgoyette 148 1.10 pgoyette static int 149 1.10 pgoyette swwdog_match(device_t parent, cfdata_t data, void *aux) 150 1.10 pgoyette { 151 1.14 pgoyette 152 1.10 pgoyette return 1; 153 1.10 pgoyette } 154 1.10 pgoyette 155 1.10 pgoyette static void 156 1.10 pgoyette swwdog_attach(device_t parent, device_t self, void *aux) 157 1.10 pgoyette { 158 1.10 pgoyette struct swwdog_softc *sc = device_private(self); 159 1.10 pgoyette 160 1.10 pgoyette sc->sc_dev = self; 161 1.10 pgoyette sc->sc_smw.smw_name = device_xname(self); 162 1.10 pgoyette sc->sc_smw.smw_cookie = sc; 163 1.10 pgoyette sc->sc_smw.smw_setmode = swwdog_setmode; 164 1.10 pgoyette sc->sc_smw.smw_tickle = swwdog_tickle; 165 1.10 pgoyette sc->sc_smw.smw_period = SWDOG_DEFAULT; 166 1.14 pgoyette 167 1.10 pgoyette callout_init(&sc->sc_c, 0); 168 1.10 pgoyette callout_setfunc(&sc->sc_c, swwdog_panic, sc); 169 1.10 pgoyette 170 1.10 pgoyette if (sysmon_wdog_register(&sc->sc_smw) == 0) 171 1.10 pgoyette aprint_normal_dev(self, "software watchdog initialized\n"); 172 1.14 pgoyette else { 173 1.10 pgoyette aprint_error_dev(self, "unable to register software " 174 1.10 pgoyette "watchdog with sysmon\n"); 175 1.14 pgoyette callout_destroy(&sc->sc_c); 176 1.14 pgoyette return; 177 1.14 pgoyette } 178 1.1 smb 179 1.10 pgoyette if (!pmf_device_register(self, swwdog_suspend, NULL)) 180 1.10 pgoyette aprint_error_dev(self, "couldn't establish power handler\n"); 181 1.10 pgoyette } 182 1.10 pgoyette 183 1.10 pgoyette static int 184 1.10 pgoyette swwdog_detach(device_t self, int flags) 185 1.10 pgoyette { 186 1.10 pgoyette struct swwdog_softc *sc = device_private(self); 187 1.10 pgoyette 188 1.13 pgoyette pmf_device_deregister(self); 189 1.10 pgoyette swwdog_disarm(sc); 190 1.14 pgoyette sysmon_wdog_unregister(&sc->sc_smw); 191 1.10 pgoyette callout_destroy(&sc->sc_c); 192 1.12 pooka workqueue_destroy(wq); 193 1.10 pgoyette 194 1.14 pgoyette return 0; 195 1.10 pgoyette } 196 1.10 pgoyette 197 1.10 pgoyette static bool 198 1.10 pgoyette swwdog_suspend(device_t dev, const pmf_qual_t *qual) 199 1.10 pgoyette { 200 1.10 pgoyette struct swwdog_softc *sc = device_private(dev); 201 1.10 pgoyette 202 1.10 pgoyette /* Don't allow suspend if watchdog is armed */ 203 1.10 pgoyette if ((sc->sc_smw.smw_mode & WDOG_MODE_MASK) != WDOG_MODE_DISARMED) 204 1.10 pgoyette return false; 205 1.10 pgoyette return true; 206 1.1 smb } 207 1.1 smb 208 1.1 smb static int 209 1.1 smb swwdog_setmode(struct sysmon_wdog *smw) 210 1.1 smb { 211 1.2 perry struct swwdog_softc *sc = smw->smw_cookie; 212 1.1 smb int error = 0; 213 1.1 smb 214 1.1 smb if ((smw->smw_mode & WDOG_MODE_MASK) == WDOG_MODE_DISARMED) { 215 1.1 smb error = swwdog_disarm(sc); 216 1.1 smb } else { 217 1.1 smb if (smw->smw_period == 0) 218 1.1 smb return EINVAL; 219 1.1 smb else if (smw->smw_period == WDOG_PERIOD_DEFAULT) 220 1.1 smb sc->sc_smw.smw_period = SWDOG_DEFAULT; 221 1.3 simonb else 222 1.3 simonb sc->sc_smw.smw_period = smw->smw_period; 223 1.1 smb error = swwdog_arm(sc); 224 1.1 smb } 225 1.1 smb return error; 226 1.1 smb } 227 1.1 smb 228 1.1 smb static int 229 1.1 smb swwdog_tickle(struct sysmon_wdog *smw) 230 1.1 smb { 231 1.1 smb 232 1.1 smb return swwdog_arm(smw->smw_cookie); 233 1.1 smb } 234 1.1 smb 235 1.1 smb static int 236 1.1 smb swwdog_arm(struct swwdog_softc *sc) 237 1.1 smb { 238 1.1 smb 239 1.1 smb callout_schedule(&sc->sc_c, sc->sc_smw.smw_period * hz); 240 1.1 smb return 0; 241 1.1 smb } 242 1.1 smb 243 1.1 smb static int 244 1.1 smb swwdog_disarm(struct swwdog_softc *sc) 245 1.1 smb { 246 1.1 smb 247 1.1 smb callout_stop(&sc->sc_c); 248 1.1 smb return 0; 249 1.1 smb } 250 1.1 smb 251 1.1 smb static void 252 1.2 perry swwdog_panic(void *vsc) 253 1.1 smb { 254 1.1 smb struct swwdog_softc *sc = vsc; 255 1.12 pooka static struct work wk; /* we'll need it max once */ 256 1.10 pgoyette bool do_panic; 257 1.1 smb 258 1.11 pooka do_panic = !swwdog_reboot; 259 1.11 pooka swwdog_reboot = false; 260 1.1 smb callout_schedule(&sc->sc_c, 60 * hz); /* deliberate double-panic */ 261 1.1 smb 262 1.14 pgoyette printf("%s: %d second timer expired\n", "swwdog", 263 1.1 smb sc->sc_smw.smw_period); 264 1.1 smb 265 1.1 smb if (do_panic) 266 1.1 smb panic("watchdog timer expired"); 267 1.3 simonb else 268 1.12 pooka workqueue_enqueue(wq, &wk, NULL); 269 1.1 smb } 270 1.10 pgoyette 271 1.22 pgoyette SYSCTL_SETUP(swwdog_sysctl_setup, "swwdog sysctl") 272 1.10 pgoyette { 273 1.10 pgoyette const struct sysctlnode *me; 274 1.10 pgoyette 275 1.22 pgoyette sysctl_createv(clog, 0, NULL, &me, CTLFLAG_READWRITE, 276 1.11 pooka CTLTYPE_NODE, "swwdog", NULL, 277 1.10 pgoyette NULL, 0, NULL, 0, 278 1.11 pooka CTL_HW, CTL_CREATE, CTL_EOL); 279 1.22 pgoyette sysctl_createv(clog, 0, NULL, NULL, CTLFLAG_READWRITE, 280 1.24 rin CTLTYPE_BOOL, "reboot", SYSCTL_DESCR("reboot if timer expires"), 281 1.11 pooka NULL, 0, &swwdog_reboot, sizeof(bool), 282 1.11 pooka CTL_HW, me->sysctl_num, CTL_CREATE, CTL_EOL); 283 1.10 pgoyette } 284 1.14 pgoyette 285 1.14 pgoyette /* 286 1.14 pgoyette * Module management 287 1.14 pgoyette */ 288 1.14 pgoyette 289 1.23 riastrad static int 290 1.14 pgoyette swwdog_init(void *arg) 291 1.14 pgoyette { 292 1.14 pgoyette /* 293 1.14 pgoyette * Merge the driver info into the kernel tables and attach the 294 1.14 pgoyette * pseudo-device 295 1.14 pgoyette */ 296 1.19 pgoyette int error = 0; 297 1.19 pgoyette 298 1.14 pgoyette 299 1.18 pgoyette #ifdef _MODULE 300 1.14 pgoyette error = config_cfdriver_attach(&swwdog_cd); 301 1.14 pgoyette if (error) { 302 1.14 pgoyette aprint_error("%s: unable to attach cfdriver\n", 303 1.14 pgoyette swwdog_cd.cd_name); 304 1.14 pgoyette return error; 305 1.14 pgoyette } 306 1.14 pgoyette error = swwdogattach(1); 307 1.14 pgoyette if (error) { 308 1.14 pgoyette aprint_error("%s: device attach failed\n", swwdog_cd.cd_name); 309 1.14 pgoyette config_cfdriver_detach(&swwdog_cd); 310 1.14 pgoyette } 311 1.18 pgoyette #endif 312 1.14 pgoyette 313 1.14 pgoyette return error; 314 1.14 pgoyette } 315 1.14 pgoyette 316 1.23 riastrad static int 317 1.14 pgoyette swwdog_fini(void *arg) 318 1.14 pgoyette { 319 1.14 pgoyette int error; 320 1.14 pgoyette 321 1.14 pgoyette error = config_detach(swwdog_dev, 0); 322 1.14 pgoyette 323 1.18 pgoyette #ifdef _MODULE 324 1.14 pgoyette error = config_cfattach_detach(swwdog_cd.cd_name, &swwdog_ca); 325 1.14 pgoyette if (error) 326 1.14 pgoyette aprint_error("%s: error detaching cfattach: %d\n", 327 1.14 pgoyette swwdog_cd.cd_name, error); 328 1.14 pgoyette 329 1.14 pgoyette error = config_cfdriver_detach(&swwdog_cd); 330 1.14 pgoyette if (error) 331 1.14 pgoyette aprint_error("%s: error detaching cfdriver: %d\n", 332 1.14 pgoyette swwdog_cd.cd_name, error); 333 1.18 pgoyette #endif 334 1.14 pgoyette 335 1.14 pgoyette return error; 336 1.14 pgoyette } 337 1.14 pgoyette 338 1.23 riastrad static int 339 1.14 pgoyette swwdog_modcmd(modcmd_t cmd, void *arg) 340 1.14 pgoyette { 341 1.14 pgoyette int ret; 342 1.23 riastrad 343 1.14 pgoyette switch (cmd) { 344 1.14 pgoyette case MODULE_CMD_INIT: 345 1.14 pgoyette ret = swwdog_init(arg); 346 1.14 pgoyette break; 347 1.14 pgoyette case MODULE_CMD_FINI: 348 1.14 pgoyette ret = swwdog_fini(arg); 349 1.14 pgoyette break; 350 1.14 pgoyette case MODULE_CMD_STAT: 351 1.14 pgoyette default: 352 1.14 pgoyette ret = ENOTTY; 353 1.14 pgoyette } 354 1.23 riastrad 355 1.14 pgoyette return ret; 356 1.14 pgoyette } 357