1 1708 stevel /* 2 1708 stevel * CDDL HEADER START 3 1708 stevel * 4 1708 stevel * The contents of this file are subject to the terms of the 5 1708 stevel * Common Development and Distribution License (the "License"). 6 1708 stevel * You may not use this file except in compliance with the License. 7 1708 stevel * 8 1708 stevel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 1708 stevel * or http://www.opensolaris.org/os/licensing. 10 1708 stevel * See the License for the specific language governing permissions 11 1708 stevel * and limitations under the License. 12 1708 stevel * 13 1708 stevel * When distributing Covered Code, include this CDDL HEADER in each 14 1708 stevel * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 1708 stevel * If applicable, add the following below this CDDL HEADER, with the 16 1708 stevel * fields enclosed by brackets "[]" replaced with your own identifying 17 1708 stevel * information: Portions Copyright [yyyy] [name of copyright owner] 18 1708 stevel * 19 1708 stevel * CDDL HEADER END 20 1708 stevel */ 21 1708 stevel 22 1708 stevel /* 23 7799 Richard * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24 1708 stevel * Use is subject to license terms. 25 1708 stevel */ 26 1708 stevel 27 1708 stevel /* 28 1708 stevel * ntwdt driver 29 1708 stevel * ------------ 30 1708 stevel * 31 1708 stevel * Subsystem Overview 32 1708 stevel * ------------------ 33 1708 stevel * 34 1708 stevel * This is a pseudo driver for the Netra-1280 watchdog 35 1708 stevel * timer (WDT). It provides for an *application-driven* 36 1708 stevel * WDT (AWDT), not a traditional, hardware-based WDT. A 37 1708 stevel * hardware-based feature is already present on the 38 1708 stevel * Netra-1280, and it is referred to here as the 39 1708 stevel * System WDT (SWDT). 40 1708 stevel * 41 1708 stevel * ScApp and Solaris cooperate to provide either a SWDT or 42 1708 stevel * an AWDT; they are mutually-exclusive. Once in AWDT 43 1708 stevel * mode, one can only transition to SWDT mode via a reboot. 44 1708 stevel * This obviously gives priority to the AWDT and was done 45 1708 stevel * to handle scenarios where the customer might temporarily 46 1708 stevel * terminate their wdog-app in order to do some debugging, 47 1708 stevel * or even to load a new version of the wdog-app. 48 1708 stevel * 49 1708 stevel * The wdog-app does an open() of the /dev/ntwdt device node 50 1708 stevel * and then issues ioctl's to control the state of the AWDT. 51 1708 stevel * The ioctl's are implemented by this driver. Only one 52 1708 stevel * concurrent instance of open() is allowed. On the close(), 53 1708 stevel * a watchdog timer still in progress is NOT terminated. 54 1708 stevel * This allows the global state machine to monitor the 55 1708 stevel * progress of a Solaris reboot. ScApp will reset Solaris 56 1708 stevel * (eg, send an XIR) if the actual boot/crashdump latency 57 1708 stevel * is larger than the current AWDT timeout. 58 1708 stevel * 59 1708 stevel * The rationale for implementing an AWDT (vs a SWDT) is 60 1708 stevel * that it is more sensitive to system outage scenarios than 61 1708 stevel * a SWDT. Eg, a system could be in such a failed state that 62 1708 stevel * even though its clock-interrupt could still run (and the 63 1708 stevel * SWDT's watchdog timer therefore re-armed), the system could 64 1708 stevel * in effect have a corrupt or very poor dispatch latency. 65 1708 stevel * An AWDT would be sensitive to dispatch latency issues, as 66 1708 stevel * well as problems with its own execution (eg, a hang or 67 1708 stevel * crash). 68 1708 stevel * 69 1708 stevel * Subsystem Interface Overview 70 1708 stevel * ---------------------------- 71 1708 stevel * 72 1708 stevel * This pseudo-driver does not have any 'extern' functions. 73 1708 stevel * 74 1708 stevel * All system interaction is done via the traditional driver 75 1708 stevel * entry points (eg, attach(9e), _init(9e)). 76 1708 stevel * 77 1708 stevel * All interaction with user is via the entry points in the 78 1708 stevel * 'struct cb_ops' vector (eg, open(9e), ioctl(9e), and 79 1708 stevel * close(9e)). 80 1708 stevel * 81 1708 stevel * Subsystem Implementation Overview 82 1708 stevel * --------------------------------- 83 1708 stevel * 84 1708 stevel * ScApp and Solaris (eg, ntwdt) cooperate so that a state 85 1708 stevel * machine global to ScApp and ntwdt is either in AWDT mode 86 1708 stevel * or in SWDT mode. These two peers communicate via the SBBC 87 1708 stevel * Mailbox that resides in IOSRAM (SBBC_MAILBOX_KEY). 88 1708 stevel * They use two new mailbox messages (LW8_MBOX_WDT_GET and 89 1708 stevel * LW8_MBOX_WDT_SET) and one new event (LW8_EVENT_SC_RESTARTED). 90 1708 stevel * 91 1708 stevel * ntwdt implements the AWDT by implementing a "virtual 92 1708 stevel * WDT" (VWDT). Eg, the watchdog timer is not a traditional 93 1708 stevel * counter in hardware, it is a variable in ntwdt's 94 1708 stevel * softstate. The wdog-app's actions cause changes to this 95 1708 stevel * and other variables in ntwdt's softstate. 96 1708 stevel * 97 1708 stevel * The wdog-app uses the LOMIOCDOGTIME ioctl to specify 98 1708 stevel * the number of seconds in the watchdog timeout (and 99 1708 stevel * therefore the VWDT). The wdog-app then uses the 100 1708 stevel * LOMIOCDOGCTL ioctl to enable the wdog. This causes 101 1708 stevel * ntwdt to create a Cyclic that will both decrement 102 1708 stevel * the VWDT and check to see if it has expired. To keep 103 1708 stevel * the VWDT from expiring, the wdog-app uses the 104 1708 stevel * LOMIOCDOGPAT ioctl to re-arm (or "pat") the watchdog. 105 1708 stevel * This sets the VWDT value to that specified in the 106 1708 stevel * last LOMIOCDOGTIME ioctl. The wdog-app can use the 107 1708 stevel * LOMIOCDOGSTATE ioctl to query the state of the VWDT. 108 1708 stevel * 109 1708 stevel * The wdog-app can also specify how Recovery is to be 110 1708 stevel * done. The only choice is whether to do a crashdump 111 1708 stevel * or not. If ntwdt computes a VWDT expiration, then 112 1708 stevel * ntwdt initiates the Recovery, else ScApp will. Eg, 113 1708 stevel * a hang in Solaris will be sensed by ScApp and not 114 1708 stevel * ntwdt. The wdog-app specifies the Recovery policy 115 1708 stevel * via the DOGCTL ioctl. 116 1708 stevel * 117 1708 stevel * Timeout Expiration 118 1708 stevel * ------------------ 119 1708 stevel * In our implementation, ScApp senses a watchdog 120 1708 stevel * expiration the same way it historically has: 121 1708 stevel * by reading a well-known area of IOSRAM (SBBC_TOD_KEY) 122 1708 stevel * to see if the timestamp associated with a 123 1708 stevel * Solaris-generated "heartbeat" field is older 124 1708 stevel * than the currently specified timeout (which is 125 1708 stevel * also specified in this same IOSRAM section). 126 1708 stevel * 127 1708 stevel * What is different when ntwdt is running is that 128 1708 stevel * ntwdt is responsible for updating the Heartbeat, 129 1708 stevel * and not the normal client (todsg). When ntwdt 130 1708 stevel * puts the system in AWDT mode, it disables todsg's 131 1708 stevel * updating of the Heartbeat by changing the state of 132 1708 stevel * a pair of kernel tunables (watchdog_activated and 133 1708 stevel * watchdog_enable). ntwdt then takes responsibility 134 1708 stevel * for updating the Heartbeat. It does this by 135 1708 stevel * updating the Heartbeat from the Cyclic that is 136 1708 stevel * created when the user enables the AWDT (DOGCTL) 137 1708 stevel * or specifies a new timeout value (DOGTIME). 138 1708 stevel * 139 1708 stevel * As long as the AWDT is enabled, ntwdt will update 140 1708 stevel * the real system Heartbeat. As a result, ScApp 141 1708 stevel * will conclude that Solaris is still running. If 142 1708 stevel * the user stops re-arming the VWDT or Solaris 143 1708 stevel * hangs (eg), ntwdt will stop updating the Heartbeat. 144 1708 stevel * 145 1708 stevel * Note that ntwdt computes expiration via the 146 1708 stevel * repeatedly firing Cyclic, and ScApp computes 147 1708 stevel * expiration via a cessation of Heartbeat update. 148 1708 stevel * Since Heartbeat update stops once user stops 149 1708 stevel * re-arming the VWDT (ie, DOGPAT ioctl), ntwdt 150 1708 stevel * will compute a timeout at t(x), and ScApp will 151 1708 stevel * compute a timeout at t(2x), where 'x' is the 152 1708 stevel * current timeout value. When ntwdt computes 153 1708 stevel * the expiration, ntwdt masks this asymmetry. 154 1708 stevel * 155 1708 stevel * Lifecycle Events 156 1708 stevel * ---------------- 157 1708 stevel * 158 1708 stevel * ntwdt only handles one of the coarse-grained 159 1708 stevel * "lifecycle events" (eg, entering OBP, shutdown, 160 1708 stevel * power-down, DR) that are possible during a Solaris 161 1708 stevel * session: a panic. (Note that ScApp handles one 162 1708 stevel * of the others: "entering OBP"). Other than these, 163 1708 stevel * a user choosing such a state transition must first 164 1708 stevel * use the wdog-app to disable the watchdog, else 165 1708 stevel * an expiration could occur. 166 1708 stevel * 167 1708 stevel * Solaris handles a panic by registering a handler 168 1708 stevel * that's called during the panic. The handler will 169 1708 stevel * set the watchdog timeout to the value specified 170 1708 stevel * in the NTWDT_BOOT_TIMEOUT_PROP driver Property. 171 1708 stevel * Again, this value should be greater than the actual 172 1708 stevel * Solaris reboot/crashdump latency. 173 1708 stevel * 174 1708 stevel * When the user enters OBP via the System Controller, 175 1708 stevel * ScApp will disable the watchdog (from ScApp's 176 1708 stevel * perspective), but it will not communicate this to 177 1708 stevel * ntwdt. After having exited OBP, the wdog-app can 178 1708 stevel * be used to enable or disable the watchdog (which 179 1708 stevel * will get both ScApp and ntwdt in-sync). 180 1708 stevel * 181 1708 stevel * Locking 182 1708 stevel * ------- 183 1708 stevel * 184 1708 stevel * ntwdt has code running at three interrupt levels as 185 1708 stevel * well as base level. 186 1708 stevel * 187 1708 stevel * The ioctls run at base level in User Context. The 188 1708 stevel * driver's entry points run at base level in Kernel 189 1708 stevel * Context. 190 1708 stevel * 191 1708 stevel * ntwdt's three interrupt levels are used by: 192 1708 stevel * 193 1708 stevel * o LOCK_LEVEL : 194 1708 stevel * the Cyclic used to manage the VWDT is initialized 195 1708 stevel * to CY_LOCK_LEVEL 196 1708 stevel * 197 1708 stevel * o DDI_SOFTINT_MED : 198 1708 stevel * the SBBC mailbox implementation registers the 199 1708 stevel * specified handlers at this level 200 1708 stevel * 201 1708 stevel * o DDI_SOFTINT_LOW : 202 1708 stevel * this level is used by two handlers. One handler 203 1708 stevel * is triggered by the LOCK_LEVEL Cyclic. The other 204 1708 stevel * handler is triggered by the DDI_SOFTINT_MED 205 1708 stevel * handler registered to handle SBBC mailbox events. 206 1708 stevel * 207 1708 stevel * The centralizing concept is that the ntwdt_wdog_mutex 208 1708 stevel * in the driver's softstate is initialized to have an 209 1708 stevel * interrupt-block-cookie corresponding to DDI_SOFTINT_LOW. 210 1708 stevel * 211 1708 stevel * As a result, any base level code grabs ntwdt_wdog_mutex 212 1708 stevel * before doing work. Also, any handler running at interrupt 213 1708 stevel * level higher than DDI_SOFTINT_LOW "posts down" so that 214 1708 stevel * a DDI_SOFTINT_LOW handler is responsible for executing 215 1708 stevel * the "real work". Each DDI_SOFTINT_LOW handler also 216 1708 stevel * first grabs ntwdt_wdog_mutex, and so base level is 217 1708 stevel * synchronized with all interrupt levels. 218 1708 stevel * 219 1708 stevel * Note there's another mutex in the softstate: ntwdt_mutex. 220 1708 stevel * This mutex has few responsibilities. However, this 221 1708 stevel * locking order must be followed: ntwdt_wdog_mutex is 222 1708 stevel * held first, and then ntwdt_mutex. This choice results 223 1708 stevel * from the fact that the number of dynamic call sites 224 1708 stevel * for ntwdt_wdog_mutex is MUCH greater than that of 225 1708 stevel * ntwdt_mutex. As a result, almost all uses of 226 1708 stevel * ntwdt_wdog_mutex do not even require ntwdt_mutex to 227 1708 stevel * be held, which saves resources. 228 1708 stevel * 229 1708 stevel * Driver Properties 230 1708 stevel * ----------------- 231 1708 stevel * 232 1708 stevel * "ddi-forceattach=1;" 233 1708 stevel * ------------------ 234 1708 stevel * 235 1708 stevel * Using this allows our driver to be automatically 236 1708 stevel * loaded at boot-time AND to not be removed from memory 237 1708 stevel * solely due to memory-pressure. 238 1708 stevel * 239 1708 stevel * Being loaded at boot allows ntwdt to (as soon as 240 1708 stevel * possible) tell ScApp of the current mode of the 241 1708 stevel * state-machine (eg, SWDT). This is needed for the case 242 1708 stevel * when Solaris is re-loaded while in AWDT mode; having 243 1708 stevel * Solaris communicate ASAP with ScApp reduces the duration 244 1708 stevel * of any "split-brain" scenario where ScApp and Solaris 245 1708 stevel * are not in the same mode. 246 1708 stevel * 247 1708 stevel * Having ntwdt remain in memory even after a close() 248 1708 stevel * allows ntwdt to answer any SBBC mailbox commands 249 1708 stevel * that ScApp sends (as the mailbox infrastructure is 250 1708 stevel * not torn down until ntwdt is detach()'d). Specifically, 251 1708 stevel * ScApp could be re-loaded after AWDT mode had been 252 1708 stevel * entered and the wdog-app had close()'d ntwdt. ScApp 253 1708 stevel * will then eventually send a LW8_EVENT_SC_RESTARTED 254 1708 stevel * mailbox event in order to learn the current state of 255 1708 stevel * state-machine. Having ntwdt remain loaded allows this 256 1708 stevel * event to never go unanswered. 257 1708 stevel * 258 1708 stevel * "ntwdt-boottimeout=600;" 259 1708 stevel * ---------------------- 260 1708 stevel * 261 1708 stevel * This specifies the watchdog timeout value (in seconds) to 262 1708 stevel * use when ntwdt is aware of the need to reboot/reload Solaris. 263 1708 stevel * 264 1708 stevel * ntwdt will update ScApp by setting the watchdog timeout 265 1708 stevel * to the specified number of seconds when either a) Solaris 266 1708 stevel * panics or b) the VWDT expires. Note that this is only done 267 1708 stevel * if the user has chosen to enable Reset. 268 1708 stevel * 269 1708 stevel * ntwdt boundary-checks the specified value, and if out-of-range, 270 1708 stevel * it initializes the watchdog timeout to a default value of 271 1708 stevel * NTWDT_DEFAULT_BOOT_TIMEOUT seconds. Note that this is a 272 1708 stevel * default value and is not a *minimum* value. The valid range 273 1708 stevel * for the watchdog timeout is between one second and 274 1708 stevel * NTWDT_MAX_TIMEOUT seconds, inclusive. 275 1708 stevel * 276 1708 stevel * If ntwdt-boottimeout is set to a value less than an actual 277 1708 stevel * Solaris boot's latency, ScApp will reset Solaris during boot. 278 1708 stevel * Note that a continuous series of ScApp-induced resets will 279 1708 stevel * not occur; ScApp only resets Solaris on the first transition 280 1708 stevel * into the watchdog-expired state. 281 1708 stevel */ 282 1708 stevel 283 1708 stevel #include <sys/note.h> 284 1708 stevel #include <sys/types.h> 285 1708 stevel #include <sys/callb.h> 286 1708 stevel #include <sys/stat.h> 287 1708 stevel #include <sys/conf.h> 288 1708 stevel #include <sys/ddi.h> 289 1708 stevel #include <sys/sunddi.h> 290 1708 stevel #include <sys/modctl.h> 291 1708 stevel #include <sys/ddi_impldefs.h> 292 1708 stevel #include <sys/kmem.h> 293 1708 stevel #include <sys/devops.h> 294 1708 stevel #include <sys/cyclic.h> 295 1708 stevel #include <sys/uadmin.h> 296 1708 stevel #include <sys/lw8_impl.h> 297 1708 stevel #include <sys/sgsbbc.h> 298 1708 stevel #include <sys/sgsbbc_iosram.h> 299 1708 stevel #include <sys/sgsbbc_mailbox.h> 300 1708 stevel #include <sys/todsg.h> 301 1708 stevel #include <sys/mem_config.h> 302 1708 stevel #include <sys/lom_io.h> 303 1708 stevel #include <sys/reboot.h> 304 1708 stevel #include <sys/clock.h> 305 1708 stevel 306 1708 stevel 307 1708 stevel /* 308 1708 stevel * tunables 309 1708 stevel */ 310 1708 stevel int ntwdt_disable_timeout_action = 0; 311 1708 stevel #ifdef DEBUG 312 1708 stevel /* 313 1708 stevel * tunable to simulate a Solaris hang. If is non-zero, then 314 1708 stevel * no system heartbeats ("hardware patting") will be done, 315 1708 stevel * even though all AWDT machinery is functioning OK. 316 1708 stevel */ 317 1708 stevel int ntwdt_stop_heart; 318 1708 stevel #endif 319 1708 stevel 320 1708 stevel /* 321 1708 stevel * Driver Property 322 1708 stevel */ 323 1708 stevel #define NTWDT_BOOT_TIMEOUT_PROP "ntwdt-boottimeout" 324 1708 stevel 325 1708 stevel /* 326 1708 stevel * watchdog-timeout values (in seconds): 327 1708 stevel * 328 1708 stevel * NTWDT_DEFAULT_BOOT_TIMEOUT: the default value used if 329 1708 stevel * this driver is aware of the 330 1708 stevel * reboot. 331 1708 stevel * 332 1708 stevel * NTWDT_MAX_TIMEOUT: max value settable by app (via the 333 1708 stevel * LOMIOCDOGTIME ioctl) 334 1708 stevel */ 335 1708 stevel #define NTWDT_DEFAULT_BOOT_TIMEOUT (10*60) 336 1708 stevel #define NTWDT_MAX_TIMEOUT (180*60) 337 1708 stevel 338 1708 stevel 339 1708 stevel #define NTWDT_CYCLIC_CHK_PERCENT (20) 340 1708 stevel #define NTWDT_MINOR_NODE "awdt" 341 1708 stevel #define OFFSET(base, field) ((char *)&base.field - (char *)&base) 342 1708 stevel 343 1708 stevel #define NTWDT_SUCCESS 0 344 1708 stevel #define NTWDT_FAILURE 1 345 1708 stevel 346 1708 stevel typedef struct { 347 1708 stevel callb_id_t ntwdt_panic_cb; 348 1708 stevel } ntwdt_callback_ids_t; 349 1708 stevel static ntwdt_callback_ids_t ntwdt_callback_ids; 350 1708 stevel 351 1708 stevel /* MBOX_EVENT_LW8 that is sent in IOSRAM Mailbox: */ 352 1708 stevel static lw8_event_t lw8_event; /* payload */ 353 1708 stevel static sbbc_msg_t sbbc_msg; /* message */ 354 1708 stevel 355 1708 stevel static ddi_softintr_t ntwdt_mbox_softint_id; 356 1708 stevel static ddi_softintr_t ntwdt_cyclic_softint_id; 357 1708 stevel 358 1708 stevel /* 359 1708 stevel * VWDT (i.e., Virtual Watchdog Timer) state 360 1708 stevel */ 361 1708 stevel typedef struct { 362 1708 stevel kmutex_t ntwdt_wdog_mutex; 363 1708 stevel ddi_iblock_cookie_t ntwdt_wdog_mtx_cookie; 364 1708 stevel int ntwdt_wdog_enabled; /* wdog enabled ? */ 365 1708 stevel int ntwdt_reset_enabled; /* reset enabled ? */ 366 1708 stevel int ntwdt_timer_running; /* wdog running ? */ 367 1708 stevel int ntwdt_wdog_expired; /* wdog expired ? */ 368 1708 stevel int ntwdt_is_initial_enable; /* 1st wdog-enable? */ 369 1708 stevel uint32_t ntwdt_boot_timeout; /* timeout for boot */ 370 1708 stevel uint32_t ntwdt_secs_remaining; /* expiration timer */ 371 1708 stevel uint8_t ntwdt_wdog_action; /* Reset action */ 372 1708 stevel uint32_t ntwdt_wdog_timeout; /* timeout in seconds */ 373 1708 stevel hrtime_t ntwdt_cyclic_interval; /* cyclic interval */ 374 1708 stevel cyc_handler_t ntwdt_cycl_hdlr; 375 1708 stevel cyc_time_t ntwdt_cycl_time; 376 1708 stevel kmutex_t ntwdt_event_lock; /* lock */ 377 1708 stevel uint64_t ntwdt_wdog_flags; 378 1708 stevel } ntwdt_wdog_t; 379 1708 stevel 380 1708 stevel /* ntwdt_wdog_flags */ 381 1708 stevel #define NTWDT_FLAG_SKIP_CYCLIC 0x1 /* skip next Cyclic */ 382 1708 stevel 383 1708 stevel /* macros to set/clear one bit in ntwdt_wdog_flags */ 384 1708 stevel #define NTWDT_FLAG_SET(p, f)\ 385 1708 stevel ((p)->ntwdt_wdog_flags |= NTWDT_FLAG_##f) 386 1708 stevel #define NTWDT_FLAG_CLR(p, f)\ 387 1708 stevel ((p)->ntwdt_wdog_flags &= ~NTWDT_FLAG_##f) 388 1708 stevel 389 1708 stevel 390 1708 stevel /* softstate */ 391 1708 stevel typedef struct { 392 1708 stevel kmutex_t ntwdt_mutex; 393 1708 stevel dev_info_t *ntwdt_dip; /* dip */ 394 1708 stevel int ntwdt_open_flag; /* file open ? */ 395 1708 stevel ntwdt_wdog_t *ntwdt_wdog_state; /* wdog state */ 396 1708 stevel cyclic_id_t ntwdt_cycl_id; 397 1708 stevel } ntwdt_state_t; 398 1708 stevel 399 1708 stevel static void *ntwdt_statep; /* softstate */ 400 1708 stevel static dev_info_t *ntwdt_dip; 401 1708 stevel /* 402 1708 stevel * if non-zero, then the app-wdog feature is available on 403 1708 stevel * this system configuration. 404 1708 stevel */ 405 1708 stevel static int ntwdt_watchdog_available; 406 1708 stevel /* 407 1708 stevel * if non-zero, then application has used the LOMIOCDOGCTL 408 1708 stevel * ioctl at least once in order to Enable the app-wdog. 409 1708 stevel * Also, if this is non-zero, then system is in AWDT mode, 410 1708 stevel * else it is in SWDT mode. 411 1708 stevel */ 412 1708 stevel static int ntwdt_watchdog_activated; 413 1708 stevel 414 1708 stevel #define getstate(minor) \ 415 1708 stevel ((ntwdt_state_t *)ddi_get_soft_state(ntwdt_statep, (minor))) 416 1708 stevel 417 1708 stevel static int ntwdt_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); 418 1708 stevel static int ntwdt_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); 419 1708 stevel static int ntwdt_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, 420 1708 stevel void **result); 421 1708 stevel static int ntwdt_open(dev_t *, int, int, cred_t *); 422 1708 stevel static int ntwdt_close(dev_t, int, int, cred_t *); 423 1708 stevel static int ntwdt_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 424 1708 stevel 425 1708 stevel static void ntwdt_reprogram_wd(ntwdt_state_t *); 426 1708 stevel static boolean_t ntwdt_panic_cb(void *arg, int code); 427 1708 stevel static void ntwdt_start_timer(ntwdt_state_t *); 428 1708 stevel static void ntwdt_stop_timer(void *); 429 1708 stevel static void ntwdt_stop_timer_lock(void *arg); 430 1708 stevel static void ntwdt_add_callbacks(ntwdt_state_t *ntwdt_ptr); 431 1708 stevel static void ntwdt_remove_callbacks(); 432 1708 stevel static void ntwdt_cyclic_pat(void *arg); 433 1708 stevel static void ntwdt_enforce_timeout(); 434 1708 stevel static void ntwdt_pat_hw_watchdog(); 435 1708 stevel static int ntwdt_set_cfgvar(int var, int val); 436 1708 stevel static void ntwdt_set_cfgvar_noreply(int var, int val); 437 1708 stevel static int ntwdt_read_props(ntwdt_state_t *); 438 1708 stevel static int ntwdt_add_mbox_handlers(ntwdt_state_t *); 439 1708 stevel static int ntwdt_set_hw_timeout(uint32_t period); 440 1708 stevel static int ntwdt_remove_mbox_handlers(void); 441 1708 stevel static uint_t ntwdt_event_data_handler(char *arg); 442 1708 stevel static uint_t ntwdt_mbox_softint(char *arg); 443 1708 stevel static uint_t ntwdt_cyclic_softint(char *arg); 444 1708 stevel static int ntwdt_lomcmd(int cmd, intptr_t arg); 445 1708 stevel static int ntwdt_chk_wdog_support(); 446 1708 stevel static int ntwdt_chk_sc_support(); 447 1708 stevel static int ntwdt_set_swdt_state(); 448 1708 stevel static void ntwdt_swdt_to_awdt(ntwdt_wdog_t *); 449 1708 stevel static void ntwdt_arm_vwdt(ntwdt_wdog_t *wdog_state); 450 1708 stevel #ifdef DEBUG 451 1708 stevel static int ntwdt_get_cfgvar(int var, int *val); 452 1708 stevel #endif 453 1708 stevel 454 1708 stevel struct cb_ops ntwdt_cb_ops = { 455 1708 stevel ntwdt_open, /* open */ 456 1708 stevel ntwdt_close, /* close */ 457 1708 stevel nulldev, /* strategy */ 458 1708 stevel nulldev, /* print */ 459 1708 stevel nulldev, /* dump */ 460 1708 stevel nulldev, /* read */ 461 1708 stevel nulldev, /* write */ 462 1708 stevel ntwdt_ioctl, /* ioctl */ 463 1708 stevel nulldev, /* devmap */ 464 1708 stevel nulldev, /* mmap */ 465 1708 stevel nulldev, /* segmap */ 466 1708 stevel nochpoll, /* poll */ 467 1708 stevel ddi_prop_op, /* cb_prop_op */ 468 1708 stevel NULL, /* streamtab */ 469 1708 stevel D_MP | D_NEW 470 1708 stevel }; 471 1708 stevel 472 1708 stevel static struct dev_ops ntwdt_ops = { 473 1708 stevel DEVO_REV, /* Devo_rev */ 474 1708 stevel 0, /* Refcnt */ 475 1708 stevel ntwdt_info, /* Info */ 476 1708 stevel nulldev, /* Identify */ 477 1708 stevel nulldev, /* Probe */ 478 1708 stevel ntwdt_attach, /* Attach */ 479 1708 stevel ntwdt_detach, /* Detach */ 480 1708 stevel nodev, /* Reset */ 481 1708 stevel &ntwdt_cb_ops, /* Driver operations */ 482 1708 stevel 0, /* Bus operations */ 483 1708 stevel NULL /* Power */ 484 1708 stevel }; 485 1708 stevel 486 1708 stevel static struct modldrv modldrv = { 487 1708 stevel &mod_driverops, /* This one is a driver */ 488 7799 Richard "ntwdt-Netra-T12", /* Name of the module. */ 489 1708 stevel &ntwdt_ops, /* Driver ops */ 490 1708 stevel }; 491 1708 stevel 492 1708 stevel static struct modlinkage modlinkage = { 493 1708 stevel MODREV_1, (void *)&modldrv, NULL 494 1708 stevel }; 495 1708 stevel 496 1708 stevel 497 1708 stevel /* 498 1708 stevel * Flags to set in ntwdt_debug. 499 1708 stevel * 500 1708 stevel * Use either the NTWDT_DBG or NTWDT_NDBG macros 501 1708 stevel */ 502 1708 stevel #define WDT_DBG_ENTRY 0x00000001 /* drv entry points */ 503 1708 stevel #define WDT_DBG_HEART 0x00000002 /* system heartbeat */ 504 1708 stevel #define WDT_DBG_VWDT 0x00000004 /* virtual WDT */ 505 1708 stevel #define WDT_DBG_EVENT 0x00000010 /* SBBC Mbox events */ 506 1708 stevel #define WDT_DBG_PROT 0x00000020 /* SC/Solaris protocol */ 507 1708 stevel #define WDT_DBG_IOCTL 0x00000040 /* ioctl's */ 508 1708 stevel 509 1708 stevel uint64_t ntwdt_debug; /* enables tracing of module's activity */ 510 1708 stevel 511 1708 stevel /* used in non-debug version of module */ 512 1708 stevel #define NTWDT_NDBG(flag, msg) { if ((ntwdt_debug & (flag)) != 0) \ 513 1708 stevel (void) printf msg; } 514 1708 stevel 515 1708 stevel #ifdef DEBUG 516 1708 stevel typedef struct { 517 1708 stevel uint32_t ntwdt_wd1; 518 1708 stevel uint8_t ntwdt_wd2; 519 1708 stevel } ntwdt_data_t; 520 1708 stevel 521 1708 stevel #define NTWDTIOCSTATE _IOWR('a', 0xa, ntwdt_data_t) 522 1708 stevel #define NTWDTIOCPANIC _IOR('a', 0xb, uint32_t) 523 1708 stevel 524 1708 stevel /* used in debug version of module */ 525 1708 stevel #define NTWDT_DBG(flag, msg) { if ((ntwdt_debug & (flag)) != 0) \ 526 1708 stevel (void) printf msg; } 527 1708 stevel #else 528 1708 stevel #define NTWDT_DBG(flag, msg) 529 1708 stevel #endif 530 1708 stevel 531 1708 stevel 532 1708 stevel int 533 1708 stevel _init(void) 534 1708 stevel { 535 1708 stevel int error = 0; 536 1708 stevel 537 1708 stevel NTWDT_DBG(WDT_DBG_ENTRY, ("_init")); 538 1708 stevel 539 1708 stevel /* Initialize the soft state structures */ 540 1708 stevel if ((error = ddi_soft_state_init(&ntwdt_statep, 541 1708 stevel sizeof (ntwdt_state_t), 1)) != 0) { 542 1708 stevel return (error); 543 1708 stevel } 544 1708 stevel 545 1708 stevel /* Install the loadable module */ 546 1708 stevel if ((error = mod_install(&modlinkage)) != 0) { 547 1708 stevel ddi_soft_state_fini(&ntwdt_statep); 548 1708 stevel } 549 1708 stevel return (error); 550 1708 stevel } 551 1708 stevel 552 1708 stevel int 553 1708 stevel _info(struct modinfo *modinfop) 554 1708 stevel { 555 1708 stevel NTWDT_DBG(WDT_DBG_ENTRY, ("_info")); 556 1708 stevel 557 1708 stevel return (mod_info(&modlinkage, modinfop)); 558 1708 stevel } 559 1708 stevel 560 1708 stevel int 561 1708 stevel _fini(void) 562 1708 stevel { 563 1708 stevel int error; 564 1708 stevel 565 1708 stevel NTWDT_DBG(WDT_DBG_ENTRY, ("_fini")); 566 1708 stevel 567 1708 stevel error = mod_remove(&modlinkage); 568 1708 stevel if (error == 0) { 569 1708 stevel ddi_soft_state_fini(&ntwdt_statep); 570 1708 stevel } 571 1708 stevel 572 1708 stevel return (error); 573 1708 stevel } 574 1708 stevel 575 1708 stevel static int 576 1708 stevel ntwdt_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 577 1708 stevel { 578 1708 stevel int instance; 579 1708 stevel ntwdt_state_t *ntwdt_ptr = NULL; 580 1708 stevel ntwdt_wdog_t *wdog_state = NULL; 581 1708 stevel cyc_handler_t *hdlr = NULL; 582 1708 stevel 583 1708 stevel NTWDT_DBG(WDT_DBG_ENTRY, ("attach: dip/cmd: 0x%p/%d", 584 1708 stevel dip, cmd)); 585 1708 stevel 586 1708 stevel switch (cmd) { 587 1708 stevel case DDI_ATTACH: 588 1708 stevel break; 589 1708 stevel 590 1708 stevel case DDI_RESUME: 591 1708 stevel return (DDI_SUCCESS); 592 1708 stevel 593 1708 stevel default: 594 1708 stevel return (DDI_FAILURE); 595 1708 stevel } 596 1708 stevel 597 1708 stevel /* see if app-wdog is supported on our config */ 598 1708 stevel if (ntwdt_chk_wdog_support() != 0) 599 1708 stevel return (DDI_FAILURE); 600 1708 stevel 601 1708 stevel /* (unsolicitedly) send SWDT state to ScApp via mailbox */ 602 1708 stevel ntwdt_set_swdt_state(); 603 1708 stevel 604 1708 stevel instance = ddi_get_instance(dip); 605 1708 stevel ASSERT(instance == 0); 606 1708 stevel 607 1708 stevel if (ddi_soft_state_zalloc(ntwdt_statep, instance) 608 1708 stevel != DDI_SUCCESS) { 609 1708 stevel return (DDI_FAILURE); 610 1708 stevel } 611 1708 stevel ntwdt_ptr = ddi_get_soft_state(ntwdt_statep, instance); 612 1708 stevel ASSERT(ntwdt_ptr != NULL); 613 1708 stevel 614 1708 stevel ntwdt_dip = dip; 615 1708 stevel 616 1708 stevel ntwdt_ptr->ntwdt_dip = dip; 617 1708 stevel ntwdt_ptr->ntwdt_cycl_id = CYCLIC_NONE; 618 1708 stevel mutex_init(&ntwdt_ptr->ntwdt_mutex, NULL, 619 1708 stevel MUTEX_DRIVER, NULL); 620 1708 stevel 621 1708 stevel /* 622 1708 stevel * Initialize the watchdog structure 623 1708 stevel */ 624 1708 stevel ntwdt_ptr->ntwdt_wdog_state = 625 1708 stevel kmem_zalloc(sizeof (ntwdt_wdog_t), KM_SLEEP); 626 1708 stevel wdog_state = ntwdt_ptr->ntwdt_wdog_state; 627 1708 stevel 628 1708 stevel /* 629 1708 stevel * Create an iblock-cookie so that ntwdt_wdog_mutex can be 630 1708 stevel * used at User Context and Interrupt Context. 631 1708 stevel */ 632 1708 stevel if (ddi_get_soft_iblock_cookie(dip, DDI_SOFTINT_LOW, 633 1708 stevel &wdog_state->ntwdt_wdog_mtx_cookie) != DDI_SUCCESS) { 634 1708 stevel cmn_err(CE_WARN, "init of iblock cookie failed " 635 1708 stevel "for ntwdt_wdog_mutex"); 636 1708 stevel goto err1; 637 1708 stevel } else { 638 1708 stevel mutex_init(&wdog_state->ntwdt_wdog_mutex, NULL, MUTEX_DRIVER, 639 1708 stevel (void *)wdog_state->ntwdt_wdog_mtx_cookie); 640 1708 stevel } 641 1708 stevel 642 1708 stevel mutex_init(&wdog_state->ntwdt_event_lock, NULL, 643 1708 stevel MUTEX_DRIVER, NULL); 644 1708 stevel 645 1708 stevel /* Cyclic fires once per second: */ 646 1708 stevel wdog_state->ntwdt_cyclic_interval = NANOSEC; 647 1708 stevel 648 1708 stevel /* interpret our .conf file. */ 649 1708 stevel (void) ntwdt_read_props(ntwdt_ptr); 650 1708 stevel 651 1708 stevel /* init the Cyclic that drives the VWDT */ 652 1708 stevel hdlr = &wdog_state->ntwdt_cycl_hdlr; 653 1708 stevel hdlr->cyh_level = CY_LOCK_LEVEL; 654 1708 stevel hdlr->cyh_func = ntwdt_cyclic_pat; 655 1708 stevel hdlr->cyh_arg = (void *)ntwdt_ptr; 656 1708 stevel 657 1708 stevel /* Register handler for SBBC Mailbox events */ 658 1708 stevel if (ntwdt_add_mbox_handlers(ntwdt_ptr) != DDI_SUCCESS) 659 1708 stevel goto err2; 660 1708 stevel 661 1708 stevel /* Softint that will be triggered by Cyclic that drives VWDT */ 662 1708 stevel if (ddi_add_softintr(dip, DDI_SOFTINT_LOW, &ntwdt_cyclic_softint_id, 663 1708 stevel NULL, NULL, ntwdt_cyclic_softint, (caddr_t)ntwdt_ptr) 664 1708 stevel != DDI_SUCCESS) { 665 1708 stevel cmn_err(CE_WARN, "failed to add cyclic softintr"); 666 1708 stevel goto err3; 667 1708 stevel } 668 1708 stevel 669 1708 stevel /* Register callbacks for various system events, e.g. panic */ 670 1708 stevel ntwdt_add_callbacks(ntwdt_ptr); 671 1708 stevel 672 1708 stevel /* 673 1708 stevel * Create Minor Node as last activity. This prevents 674 1708 stevel * application from accessing our implementation until it 675 1708 stevel * is initialized. 676 1708 stevel */ 677 1708 stevel if (ddi_create_minor_node(dip, NTWDT_MINOR_NODE, S_IFCHR, 0, 678 1708 stevel DDI_PSEUDO, NULL) == DDI_FAILURE) { 679 1708 stevel cmn_err(CE_WARN, "failed to create Minor Node: %s", 680 1708 stevel NTWDT_MINOR_NODE); 681 1708 stevel goto err4; 682 1708 stevel } 683 1708 stevel 684 1708 stevel /* Display our driver info in the banner */ 685 1708 stevel ddi_report_dev(dip); 686 1708 stevel 687 1708 stevel return (DDI_SUCCESS); 688 1708 stevel 689 1708 stevel err4: 690 1708 stevel ntwdt_remove_callbacks(); 691 1708 stevel ddi_remove_softintr(ntwdt_cyclic_softint_id); 692 1708 stevel err3: 693 1708 stevel ntwdt_remove_mbox_handlers(); 694 1708 stevel err2: 695 1708 stevel mutex_destroy(&wdog_state->ntwdt_event_lock); 696 1708 stevel mutex_destroy(&wdog_state->ntwdt_wdog_mutex); 697 1708 stevel err1: 698 1708 stevel kmem_free(wdog_state, sizeof (ntwdt_wdog_t)); 699 1708 stevel ntwdt_ptr->ntwdt_wdog_state = NULL; 700 1708 stevel 701 1708 stevel mutex_destroy(&ntwdt_ptr->ntwdt_mutex); 702 1708 stevel ddi_soft_state_free(ntwdt_statep, instance); 703 1708 stevel 704 1708 stevel ntwdt_dip = NULL; 705 1708 stevel 706 1708 stevel return (DDI_FAILURE); 707 1708 stevel } 708 1708 stevel 709 1708 stevel /* 710 1708 stevel * Do static checks to see if the app-wdog feature is supported in 711 1708 stevel * the current configuration. 712 1708 stevel * 713 1708 stevel * If the kernel debugger was booted, then we disallow the app-wdog 714 1708 stevel * feature, as we assume the user will be interested more in 715 1708 stevel * debuggability of system than its ability to support an app-wdog. 716 1708 stevel * (Note that the System Watchdog (SWDT) can still be available). 717 1708 stevel * 718 1708 stevel * If the currently loaded version of ScApp does not understand one 719 1708 stevel * of the IOSRAM mailbox messages that is specific to the app-wdog 720 1708 stevel * protocol, then we disallow use of the app-wdog feature (else 721 1708 stevel * we could have a "split-brain" scenario where Solaris supports 722 1708 stevel * app-wdog but ScApp doesn't). 723 1708 stevel * 724 1708 stevel * Note that there is no *dynamic* checking of whether ScApp supports 725 1708 stevel * the wdog protocol. Eg, if a new version of ScApp was loaded out 726 1708 stevel * from under Solaris, then once in AWDT mode, Solaris has no way 727 1708 stevel * of knowing that (a possibly older version of) ScApp was loaded. 728 1708 stevel */ 729 1708 stevel static int 730 1708 stevel ntwdt_chk_wdog_support() 731 1708 stevel { 732 1708 stevel int retval = ENOTSUP; 733 1708 stevel int rv; 734 1708 stevel 735 1708 stevel if ((boothowto & RB_DEBUG) != 0) { 736 1708 stevel cmn_err(CE_WARN, "kernel debugger was booted; " 737 1708 stevel "application watchdog is not available."); 738 1708 stevel return (retval); 739 1708 stevel } 740 1708 stevel 741 1708 stevel /* 742 1708 stevel * if ScApp does not support the MBOX_GET cmd, then 743 1708 stevel * it does not support the app-wdog feature. Also, 744 1708 stevel * if there is *any* type of SBBC Mailbox error at 745 1708 stevel * this point, we will disable the app watchdog 746 1708 stevel * feature. 747 1708 stevel */ 748 1708 stevel if ((rv = ntwdt_chk_sc_support()) != 0) { 749 1708 stevel if (rv == EINVAL) 750 1708 stevel cmn_err(CE_WARN, "ScApp does not support " 751 1708 stevel "the application watchdog feature."); 752 1708 stevel else 753 1708 stevel cmn_err(CE_WARN, "SBBC mailbox had error;" 754 1708 stevel "application watchdog is not available."); 755 1708 stevel retval = rv; 756 1708 stevel } else { 757 1708 stevel ntwdt_watchdog_available = 1; 758 1708 stevel retval = 0; 759 1708 stevel } 760 1708 stevel 761 1708 stevel NTWDT_DBG(WDT_DBG_PROT, ("app-wdog is %savailable", 762 1708 stevel (ntwdt_watchdog_available != 0) ? "" : "not ")); 763 1708 stevel 764 1708 stevel return (retval); 765 1708 stevel } 766 1708 stevel 767 1708 stevel /* 768 1708 stevel * Check to see if ScApp supports the app-watchdog feature. 769 1708 stevel * 770 1708 stevel * Do this by sending one of the mailbox commands that is 771 1708 stevel * specific to the app-wdog protocol. If ScApp does not 772 1708 stevel * return an error code, we will assume it understands it 773 1708 stevel * (as well as the remainder of the app-wdog protocol). 774 1708 stevel * 775 1708 stevel * Notes: 776 1708 stevel * ntwdt_lomcmd() will return EINVAL if ScApp does not 777 1708 stevel * understand the message. The underlying sbbc_mbox_ 778 1708 stevel * utility function returns SG_MBOX_STATUS_ILLEGAL_PARAMETER 779 1708 stevel * ("illegal ioctl parameter"). 780 1708 stevel */ 781 1708 stevel static int 782 1708 stevel ntwdt_chk_sc_support() 783 1708 stevel { 784 1708 stevel lw8_get_wdt_t get_wdt; 785 1708 stevel 786 1708 stevel return (ntwdt_lomcmd(LW8_MBOX_WDT_GET, (intptr_t)&get_wdt)); 787 1708 stevel } 788 1708 stevel 789 1708 stevel static int 790 1708 stevel ntwdt_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 791 1708 stevel { 792 1708 stevel int instance = ddi_get_instance(dip); 793 1708 stevel ntwdt_state_t *ntwdt_ptr = NULL; 794 1708 stevel 795 1708 stevel NTWDT_DBG(WDT_DBG_ENTRY, ("detach: dip/cmd: 0x%p/%d", 796 1708 stevel dip, cmd)); 797 1708 stevel 798 1708 stevel ntwdt_ptr = ddi_get_soft_state(ntwdt_statep, instance); 799 1708 stevel if (ntwdt_ptr == NULL) { 800 1708 stevel return (DDI_FAILURE); 801 1708 stevel } 802 1708 stevel 803 1708 stevel switch (cmd) { 804 1708 stevel case DDI_SUSPEND: 805 1708 stevel return (DDI_SUCCESS); 806 1708 stevel 807 1708 stevel case DDI_DETACH: 808 1708 stevel /* 809 1708 stevel * release resources in opposite (LIFO) order as 810 1708 stevel * were allocated in attach(9f). 811 1708 stevel */ 812 1708 stevel ddi_remove_minor_node(dip, NULL); 813 1708 stevel 814 1708 stevel ntwdt_stop_timer_lock((void *)ntwdt_ptr); 815 1708 stevel 816 1708 stevel ntwdt_remove_callbacks(ntwdt_ptr); 817 1708 stevel 818 1708 stevel ddi_remove_softintr(ntwdt_cyclic_softint_id); 819 1708 stevel 820 1708 stevel ntwdt_remove_mbox_handlers(); 821 1708 stevel 822 1708 stevel mutex_destroy(&ntwdt_ptr->ntwdt_wdog_state->ntwdt_event_lock); 823 1708 stevel mutex_destroy(&ntwdt_ptr->ntwdt_wdog_state->ntwdt_wdog_mutex); 824 1708 stevel kmem_free(ntwdt_ptr->ntwdt_wdog_state, 825 1708 stevel sizeof (ntwdt_wdog_t)); 826 1708 stevel ntwdt_ptr->ntwdt_wdog_state = NULL; 827 1708 stevel 828 1708 stevel mutex_destroy(&ntwdt_ptr->ntwdt_mutex); 829 1708 stevel 830 1708 stevel ddi_soft_state_free(ntwdt_statep, instance); 831 1708 stevel 832 1708 stevel ntwdt_dip = NULL; 833 1708 stevel return (DDI_SUCCESS); 834 1708 stevel 835 1708 stevel default: 836 1708 stevel return (DDI_FAILURE); 837 1708 stevel } 838 1708 stevel } 839 1708 stevel 840 1708 stevel /* 841 1708 stevel * Register the SBBC Mailbox handlers. 842 1708 stevel * 843 1708 stevel * Currently, only one handler is used. It processes the MBOX_EVENT_LW8 844 1708 stevel * Events that are sent by ScApp. Of the Events that are sent, only 845 1708 stevel * the Event declaring that ScApp is coming up from a reboot 846 1708 stevel * (LW8_EVENT_SC_RESTARTED) is processed. 847 1708 stevel * 848 1708 stevel * sbbc_mbox_reg_intr registers the handler so that it executes at 849 1708 stevel * a DDI_SOFTINT_MED priority. 850 1708 stevel */ 851 1708 stevel static int 852 1708 stevel ntwdt_add_mbox_handlers(ntwdt_state_t *ntwdt_ptr) 853 1708 stevel { 854 1708 stevel int err; 855 1708 stevel 856 1708 stevel /* 857 1708 stevel * We need two interrupt handlers to handle the SBBC mbox 858 1708 stevel * events. The sbbc_mbox_xxx implementation will 859 1708 stevel * trigger our ntwdt_event_data_handler, which itself will 860 1708 stevel * trigger our ntwdt_mbox_softint. As a result, we'll 861 1708 stevel * register ntwdt_mbox_softint first, to ensure it cannot 862 1708 stevel * be called (until its caller, ntwdt_event_data_handler) 863 1708 stevel * is registered. 864 1708 stevel */ 865 1708 stevel 866 1708 stevel /* 867 1708 stevel * add the softint that will do the real work of handling the 868 1708 stevel * LW8_SC_RESTARTED_EVENT sent from ScApp. 869 1708 stevel */ 870 1708 stevel if (ddi_add_softintr(ntwdt_ptr->ntwdt_dip, DDI_SOFTINT_LOW, 871 1708 stevel &ntwdt_mbox_softint_id, NULL, NULL, ntwdt_mbox_softint, 872 1708 stevel (caddr_t)ntwdt_ptr) != DDI_SUCCESS) { 873 1708 stevel cmn_err(CE_WARN, "Failed to add MBOX_EVENT_LW8 softintr"); 874 1708 stevel return (DDI_FAILURE); 875 1708 stevel } 876 1708 stevel 877 1708 stevel /* 878 1708 stevel * Register an interrupt handler with the SBBC mailbox utility. 879 1708 stevel * This handler will get called on each event of each type of 880 1708 stevel * MBOX_EVENT_LW8 events. However, it will only conditionally 881 1708 stevel * trigger the worker-handler (ntwdt_mbox_softintr). 882 1708 stevel */ 883 1708 stevel sbbc_msg.msg_buf = (caddr_t)&lw8_event; 884 1708 stevel sbbc_msg.msg_len = sizeof (lw8_event); 885 1708 stevel 886 1708 stevel err = sbbc_mbox_reg_intr(MBOX_EVENT_LW8, ntwdt_event_data_handler, 887 1708 stevel &sbbc_msg, NULL, &ntwdt_ptr->ntwdt_wdog_state->ntwdt_event_lock); 888 1708 stevel if (err != 0) { 889 1708 stevel cmn_err(CE_WARN, "Failed to register SBBC MBOX_EVENT_LW8" 890 1708 stevel " handler. err=%d", err); 891 1708 stevel 892 1708 stevel ddi_remove_softintr(ntwdt_mbox_softint_id); 893 1708 stevel return (DDI_FAILURE); 894 1708 stevel } 895 1708 stevel 896 1708 stevel return (DDI_SUCCESS); 897 1708 stevel } 898 1708 stevel 899 1708 stevel /* 900 1708 stevel * Unregister the SBBC Mailbox handlers that were registered 901 1708 stevel * by ntwdt_add_mbox_handlers. 902 1708 stevel */ 903 1708 stevel static int 904 1708 stevel ntwdt_remove_mbox_handlers(void) 905 1708 stevel { 906 1708 stevel int rv = DDI_SUCCESS; 907 1708 stevel int err; 908 1708 stevel 909 1708 stevel /* 910 1708 stevel * unregister the two handlers that cooperate to handle 911 1708 stevel * the LW8_SC_RESTARTED_EVENT. Note that they are unregistered 912 1708 stevel * in LIFO order (as compared to how they were registered). 913 1708 stevel */ 914 1708 stevel err = sbbc_mbox_unreg_intr(MBOX_EVENT_LW8, ntwdt_event_data_handler); 915 1708 stevel if (err != 0) { 916 1708 stevel cmn_err(CE_WARN, "Failed to unregister sbbc MBOX_EVENT_LW8 " 917 1708 stevel "handler. Err=%d", err); 918 1708 stevel rv = DDI_FAILURE; 919 1708 stevel } 920 1708 stevel 921 1708 stevel /* remove the associated softint */ 922 1708 stevel ddi_remove_softintr(ntwdt_mbox_softint_id); 923 1708 stevel 924 1708 stevel return (rv); 925 1708 stevel } 926 1708 stevel 927 1708 stevel _NOTE(ARGSUSED(0)) 928 1708 stevel static int 929 1708 stevel ntwdt_info(dev_info_t *dip, ddi_info_cmd_t infocmd, 930 1708 stevel void *arg, void **result) 931 1708 stevel { 932 1708 stevel dev_t dev; 933 1708 stevel int instance; 934 1708 stevel int error = DDI_SUCCESS; 935 1708 stevel 936 1708 stevel if (result == NULL) 937 1708 stevel return (DDI_FAILURE); 938 1708 stevel 939 1708 stevel switch (infocmd) { 940 1708 stevel case DDI_INFO_DEVT2DEVINFO: 941 1708 stevel dev = (dev_t)arg; 942 1708 stevel if (getminor(dev) == 0) 943 1708 stevel *result = (void *)ntwdt_dip; 944 1708 stevel else 945 1708 stevel error = DDI_FAILURE; 946 1708 stevel break; 947 1708 stevel 948 1708 stevel case DDI_INFO_DEVT2INSTANCE: 949 1708 stevel dev = (dev_t)arg; 950 1708 stevel instance = getminor(dev); 951 1708 stevel *result = (void *)(uintptr_t)instance; 952 1708 stevel break; 953 1708 stevel 954 1708 stevel default: 955 1708 stevel error = DDI_FAILURE; 956 1708 stevel } 957 1708 stevel 958 1708 stevel return (error); 959 1708 stevel } 960 1708 stevel 961 1708 stevel /* 962 1708 stevel * Open the device this driver manages. 963 1708 stevel * 964 1708 stevel * Ensure the caller is a privileged process, else 965 1708 stevel * a non-privileged user could cause denial-of-service 966 1708 stevel * and/or negatively impact reliability/availability. 967 1708 stevel * 968 1708 stevel * Ensure there is only one concurrent open(). 969 1708 stevel */ 970 1708 stevel _NOTE(ARGSUSED(1)) 971 1708 stevel static int 972 1708 stevel ntwdt_open(dev_t *devp, int flag, int otyp, cred_t *credp) 973 1708 stevel { 974 1708 stevel int inst = getminor(*devp); 975 1708 stevel int ret = 0; 976 1708 stevel ntwdt_state_t *ntwdt_ptr = getstate(inst); 977 1708 stevel 978 1708 stevel NTWDT_DBG(WDT_DBG_ENTRY, ("open: inst/soft: %d/0x%p", 979 1708 stevel inst, ntwdt_ptr)); 980 1708 stevel 981 1708 stevel /* ensure caller is a privileged process */ 982 1708 stevel if (drv_priv(credp) != 0) 983 1708 stevel return (EPERM); 984 1708 stevel 985 1708 stevel /* 986 1708 stevel * Check for a Deferred Attach scenario. 987 1708 stevel * Return ENXIO so DDI framework will call 988 1708 stevel * attach() and then retry the open(). 989 1708 stevel */ 990 1708 stevel if (ntwdt_ptr == NULL) 991 1708 stevel return (ENXIO); 992 1708 stevel 993 1708 stevel mutex_enter(&ntwdt_ptr->ntwdt_wdog_state->ntwdt_wdog_mutex); 994 1708 stevel mutex_enter(&ntwdt_ptr->ntwdt_mutex); 995 1708 stevel if (ntwdt_ptr->ntwdt_open_flag != 0) 996 1708 stevel ret = EAGAIN; 997 1708 stevel else 998 1708 stevel ntwdt_ptr->ntwdt_open_flag = 1; 999 1708 stevel mutex_exit(&ntwdt_ptr->ntwdt_mutex); 1000 1708 stevel mutex_exit(&ntwdt_ptr->ntwdt_wdog_state->ntwdt_wdog_mutex); 1001 1708 stevel 1002 1708 stevel return (ret); 1003 1708 stevel } 1004 1708 stevel 1005 1708 stevel /* 1006 1708 stevel * Close the device this driver manages. 1007 1708 stevel * 1008 1708 stevel * Notes: 1009 1708 stevel * 1010 1708 stevel * The close() can happen while the AWDT is running ! 1011 1708 stevel * (and nothing is done, eg, to disable the watchdog 1012 1708 stevel * or to stop updating the system heartbeat). This 1013 1708 stevel * is the desired behavior, as this allows for the 1014 1708 stevel * case of monitoring a Solaris reboot in terms 1015 1708 stevel * of watchdog expiration. 1016 1708 stevel */ 1017 1708 stevel _NOTE(ARGSUSED(1)) 1018 1708 stevel static int 1019 1708 stevel ntwdt_close(dev_t dev, int flag, int otyp, cred_t *credp) 1020 1708 stevel { 1021 1708 stevel int inst = getminor(dev); 1022 1708 stevel ntwdt_state_t *ntwdt_ptr = getstate(inst); 1023 1708 stevel 1024 1708 stevel NTWDT_DBG(WDT_DBG_ENTRY, ("close: inst/soft: %d/0x%p", 1025 1708 stevel inst, ntwdt_ptr)); 1026 1708 stevel 1027 1708 stevel if (ntwdt_ptr == NULL) 1028 1708 stevel return (ENXIO); 1029 1708 stevel 1030 1708 stevel mutex_enter(&ntwdt_ptr->ntwdt_wdog_state->ntwdt_wdog_mutex); 1031 1708 stevel mutex_enter(&ntwdt_ptr->ntwdt_mutex); 1032 1708 stevel if (ntwdt_ptr->ntwdt_open_flag != 0) { 1033 1708 stevel ntwdt_ptr->ntwdt_open_flag = 0; 1034 1708 stevel } 1035 1708 stevel mutex_exit(&ntwdt_ptr->ntwdt_mutex); 1036 1708 stevel mutex_exit(&ntwdt_ptr->ntwdt_wdog_state->ntwdt_wdog_mutex); 1037 1708 stevel 1038 1708 stevel return (0); 1039 1708 stevel } 1040 1708 stevel 1041 1708 stevel _NOTE(ARGSUSED(4)) 1042 1708 stevel static int 1043 1708 stevel ntwdt_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, 1044 1708 stevel cred_t *credp, int *rvalp) 1045 1708 stevel { 1046 1708 stevel int inst = getminor(dev); 1047 1708 stevel int retval = 0; 1048 1708 stevel ntwdt_state_t *ntwdt_ptr = NULL; 1049 1708 stevel ntwdt_wdog_t *wdog_state; 1050 1708 stevel 1051 1708 stevel if ((ntwdt_ptr = getstate(inst)) == NULL) 1052 1708 stevel return (ENXIO); 1053 1708 stevel 1054 1708 stevel /* Only allow ioctl's if Solaris/ScApp support app-wdog */ 1055 1708 stevel if (ntwdt_watchdog_available == 0) 1056 1708 stevel return (ENXIO); 1057 1708 stevel 1058 1708 stevel wdog_state = ntwdt_ptr->ntwdt_wdog_state; 1059 1708 stevel 1060 1708 stevel switch (cmd) { 1061 1708 stevel case LOMIOCDOGSTATE: { 1062 1708 stevel /* 1063 1708 stevel * Return the state of the AWDT to the application. 1064 1708 stevel */ 1065 1708 stevel lom_dogstate_t lom_dogstate; 1066 1708 stevel 1067 1708 stevel mutex_enter(&wdog_state->ntwdt_wdog_mutex); 1068 1708 stevel lom_dogstate.reset_enable = 1069 1708 stevel wdog_state->ntwdt_reset_enabled; 1070 1708 stevel lom_dogstate.dog_enable = 1071 1708 stevel wdog_state->ntwdt_wdog_enabled; 1072 1708 stevel lom_dogstate.dog_timeout = 1073 1708 stevel wdog_state->ntwdt_wdog_timeout; 1074 1708 stevel mutex_exit(&wdog_state->ntwdt_wdog_mutex); 1075 1708 stevel 1076 1708 stevel NTWDT_DBG(WDT_DBG_IOCTL, ("DOGSTATE: wdog/reset/timeout:" 1077 1708 stevel " %d/%d/%d", lom_dogstate.dog_enable, 1078 1708 stevel lom_dogstate.reset_enable, lom_dogstate.dog_timeout)); 1079 1708 stevel 1080 1708 stevel if (ddi_copyout((caddr_t)&lom_dogstate, (caddr_t)arg, 1081 1708 stevel sizeof (lom_dogstate_t), mode) != 0) { 1082 1708 stevel retval = EFAULT; 1083 1708 stevel } 1084 1708 stevel break; 1085 1708 stevel } 1086 1708 stevel 1087 1708 stevel case LOMIOCDOGCTL: { 1088 1708 stevel /* 1089 1708 stevel * Allow application to control whether watchdog 1090 1708 stevel * is {dis,en}abled and whether Reset is 1091 1708 stevel * {dis,en}abled. 1092 1708 stevel */ 1093 1708 stevel lom_dogctl_t lom_dogctl; 1094 1708 stevel 1095 1708 stevel if (ddi_copyin((caddr_t)arg, (caddr_t)&lom_dogctl, 1096 1708 stevel sizeof (lom_dogctl_t), mode) != 0) { 1097 1708 stevel retval = EFAULT; 1098 1708 stevel break; 1099 1708 stevel } 1100 1708 stevel 1101 1708 stevel NTWDT_DBG(WDT_DBG_IOCTL, ("DOGCTL: wdog/reset:" 1102 1708 stevel " %d/%d", lom_dogctl.dog_enable, 1103 1708 stevel lom_dogctl.reset_enable)); 1104 1708 stevel 1105 1708 stevel mutex_enter(&wdog_state->ntwdt_wdog_mutex); 1106 1708 stevel 1107 1708 stevel if (wdog_state->ntwdt_wdog_timeout == 0) { 1108 1708 stevel /* 1109 1708 stevel * then LOMIOCDOGTIME has never been used 1110 1708 stevel * to setup a valid timeout. 1111 1708 stevel */ 1112 1708 stevel retval = EINVAL; 1113 1708 stevel goto end; 1114 1708 stevel } 1115 1708 stevel 1116 1708 stevel /* 1117 1708 stevel * Return error for the non-sensical combination: 1118 1708 stevel * "enable Reset" and "disable watchdog". 1119 1708 stevel */ 1120 1708 stevel if (lom_dogctl.dog_enable == 0 && 1121 1708 stevel lom_dogctl.reset_enable != 0) { 1122 1708 stevel retval = EINVAL; 1123 1708 stevel goto end; 1124 1708 stevel } 1125 1708 stevel 1126 1708 stevel /* 1127 1708 stevel * Store the user-specified state in our softstate. 1128 1708 stevel * Note that our implementation here is stateless. 1129 1708 stevel * Eg, we do not disallow an "enable the watchdog" 1130 1708 stevel * command when the watchdog is currently enabled. 1131 1708 stevel * This is needed (at least in the case) when 1132 1708 stevel * the user enters OBP via ScApp/lom. In that case, 1133 1708 stevel * ScApp disables the watchdog, but does not inform 1134 1708 stevel * Solaris. As a result, an ensuing, unfiltered DOGCTL 1135 1708 stevel * to enable the watchdog is required. 1136 1708 stevel */ 1137 1708 stevel wdog_state->ntwdt_reset_enabled = 1138 1708 stevel lom_dogctl.reset_enable; 1139 1708 stevel wdog_state->ntwdt_wdog_enabled = 1140 1708 stevel lom_dogctl.dog_enable; 1141 1708 stevel 1142 1708 stevel if (wdog_state->ntwdt_wdog_enabled != 0) { 1143 1708 stevel /* 1144 1708 stevel * then user wants to enable watchdog. 1145 1708 stevel * Arm the watchdog timer and start the 1146 1708 stevel * Cyclic, if it is not running. 1147 1708 stevel */ 1148 1708 stevel ntwdt_arm_vwdt(wdog_state); 1149 1708 stevel 1150 1708 stevel if (wdog_state->ntwdt_timer_running == 0) { 1151 1708 stevel ntwdt_start_timer(ntwdt_ptr); 1152 1708 stevel } 1153 1708 stevel } else { 1154 1708 stevel /* 1155 1708 stevel * user wants to disable the watchdog. 1156 1708 stevel * Note that we do not set ntwdt_secs_remaining 1157 1708 stevel * to zero; that could cause a false expiration. 1158 1708 stevel */ 1159 1708 stevel if (wdog_state->ntwdt_timer_running != 0) { 1160 1708 stevel ntwdt_stop_timer(ntwdt_ptr); 1161 1708 stevel } 1162 1708 stevel } 1163 1708 stevel 1164 1708 stevel /* 1165 1708 stevel * Send a permutation of mailbox commands to 1166 1708 stevel * ScApp that describes the current state of the 1167 1708 stevel * watchdog timer. Note that the permutation 1168 1708 stevel * depends on whether this is the first 1169 1708 stevel * Enabling of the watchdog or not. 1170 1708 stevel */ 1171 1708 stevel if (wdog_state->ntwdt_wdog_enabled != 0 && 1172 1708 stevel wdog_state->ntwdt_is_initial_enable == 0) { 1173 1708 stevel 1174 1708 stevel /* switch from SWDT to AWDT mode */ 1175 1708 stevel ntwdt_swdt_to_awdt(wdog_state); 1176 1708 stevel 1177 1708 stevel /* Tell ScApp we're in AWDT mode */ 1178 1708 stevel ntwdt_set_cfgvar(LW8_WDT_PROP_MODE, 1179 1708 stevel LW8_PROP_MODE_AWDT); 1180 1708 stevel } 1181 1708 stevel 1182 1708 stevel /* Inform ScApp of the choices made by the app */ 1183 1708 stevel ntwdt_set_cfgvar(LW8_WDT_PROP_WDT, 1184 1708 stevel wdog_state->ntwdt_wdog_enabled); 1185 1708 stevel ntwdt_set_cfgvar(LW8_WDT_PROP_RECOV, 1186 1708 stevel wdog_state->ntwdt_reset_enabled); 1187 1708 stevel 1188 1708 stevel if (wdog_state->ntwdt_wdog_enabled != 0 && 1189 1708 stevel wdog_state->ntwdt_is_initial_enable == 0) { 1190 1708 stevel /* 1191 1708 stevel * Clear tod_iosram_t.tod_timeout_period, 1192 1708 stevel * which is used in SWDT part of state 1193 1708 stevel * machine. (If this field is non-zero, 1194 1708 stevel * ScApp assumes that Solaris' SWDT is active). 1195 1708 stevel * 1196 1708 stevel * Clearing this is useful in case SC reboots 1197 1708 stevel * while Solaris is running, as ScApp will read 1198 1708 stevel * a zero and not assume SWDT is running. 1199 1708 stevel */ 1200 1708 stevel ntwdt_set_hw_timeout(0); 1201 1708 stevel 1202 1708 stevel /* "the first watchdog-enable has been seen" */ 1203 1708 stevel wdog_state->ntwdt_is_initial_enable = 1; 1204 1708 stevel } 1205 1708 stevel 1206 1708 stevel mutex_exit(&wdog_state->ntwdt_wdog_mutex); 1207 1708 stevel break; 1208 1708 stevel } 1209 1708 stevel 1210 1708 stevel case LOMIOCDOGTIME: { 1211 1708 stevel /* 1212 1708 stevel * Allow application to set the period (in seconds) 1213 1708 stevel * of the watchdog timeout. 1214 1708 stevel */ 1215 1708 stevel uint32_t lom_dogtime; 1216 1708 stevel 1217 1708 stevel if (ddi_copyin((caddr_t)arg, (caddr_t)&lom_dogtime, 1218 1708 stevel sizeof (uint32_t), mode) != 0) { 1219 1708 stevel retval = EFAULT; 1220 1708 stevel break; 1221 1708 stevel } 1222 1708 stevel 1223 1708 stevel NTWDT_DBG(WDT_DBG_IOCTL, ("DOGTIME: %u seconds", 1224 1708 stevel lom_dogtime)); 1225 1708 stevel 1226 1708 stevel /* Ensure specified timeout is within range. */ 1227 1708 stevel if ((lom_dogtime == 0) || 1228 1708 stevel (lom_dogtime > NTWDT_MAX_TIMEOUT)) { 1229 1708 stevel retval = EINVAL; 1230 1708 stevel break; 1231 1708 stevel } 1232 1708 stevel 1233 1708 stevel mutex_enter(&wdog_state->ntwdt_wdog_mutex); 1234 1708 stevel 1235 1708 stevel wdog_state->ntwdt_wdog_timeout = lom_dogtime; 1236 1708 stevel 1237 1708 stevel /* 1238 1708 stevel * If watchdog is currently running, re-arm the 1239 1708 stevel * watchdog timeout with the specified value. 1240 1708 stevel */ 1241 1708 stevel if (wdog_state->ntwdt_timer_running != 0) { 1242 1708 stevel ntwdt_arm_vwdt(wdog_state); 1243 1708 stevel } 1244 1708 stevel 1245 1708 stevel /* Tell ScApp of the specified timeout */ 1246 1708 stevel ntwdt_set_cfgvar(LW8_WDT_PROP_TO, lom_dogtime); 1247 1708 stevel 1248 1708 stevel mutex_exit(&wdog_state->ntwdt_wdog_mutex); 1249 1708 stevel break; 1250 1708 stevel } 1251 1708 stevel 1252 1708 stevel case LOMIOCDOGPAT: { 1253 1708 stevel /* 1254 1708 stevel * Allow user to re-arm ("pat") the watchdog. 1255 1708 stevel */ 1256 1708 stevel NTWDT_DBG(WDT_DBG_IOCTL, ("DOGPAT")); 1257 1708 stevel 1258 1708 stevel mutex_enter(&wdog_state->ntwdt_wdog_mutex); 1259 1708 stevel 1260 1708 stevel /* 1261 1708 stevel * If watchdog is not enabled or underlying 1262 1708 stevel * Cyclic timer is not running, exit. 1263 1708 stevel */ 1264 1708 stevel if (!(wdog_state->ntwdt_wdog_enabled && 1265 1708 stevel wdog_state->ntwdt_timer_running)) 1266 1708 stevel goto end; 1267 1708 stevel 1268 1708 stevel if (wdog_state->ntwdt_wdog_expired == 0) { 1269 1708 stevel /* then VWDT has not expired; re-arm it */ 1270 1708 stevel ntwdt_arm_vwdt(wdog_state); 1271 1708 stevel 1272 1708 stevel NTWDT_DBG(WDT_DBG_VWDT, ("VWDT re-armed:" 1273 1708 stevel " %d seconds", 1274 1708 stevel wdog_state->ntwdt_secs_remaining)); 1275 1708 stevel } 1276 1708 stevel 1277 1708 stevel mutex_exit(&wdog_state->ntwdt_wdog_mutex); 1278 1708 stevel break; 1279 1708 stevel } 1280 1708 stevel 1281 1708 stevel #ifdef DEBUG 1282 1708 stevel case NTWDTIOCPANIC: { 1283 1708 stevel /* 1284 1708 stevel * Use in unit/integration testing to test our 1285 1708 stevel * panic-handler code. 1286 1708 stevel */ 1287 1708 stevel cmn_err(CE_PANIC, "NTWDTIOCPANIC: force a panic"); 1288 1708 stevel break; 1289 1708 stevel } 1290 1708 stevel 1291 1708 stevel case NTWDTIOCSTATE: { 1292 1708 stevel /* 1293 1708 stevel * Allow application to read wdog state from the 1294 1708 stevel * SC (and *not* the driver's softstate). 1295 1708 stevel * 1296 1708 stevel * Return state of: 1297 1708 stevel * o recovery-enabled 1298 1708 stevel * o current timeout value 1299 1708 stevel */ 1300 1708 stevel ntwdt_data_t ntwdt_data; 1301 1708 stevel int action; 1302 1708 stevel int timeout; 1303 1708 stevel int ret; 1304 1708 stevel 1305 1708 stevel mutex_enter(&wdog_state->ntwdt_wdog_mutex); 1306 1708 stevel ret = ntwdt_get_cfgvar(LW8_WDT_PROP_TO, &timeout); 1307 1708 stevel ret |= ntwdt_get_cfgvar(LW8_WDT_PROP_RECOV, &action); 1308 1708 stevel mutex_exit(&wdog_state->ntwdt_wdog_mutex); 1309 1708 stevel 1310 1708 stevel bzero((caddr_t)&ntwdt_data, sizeof (ntwdt_data)); 1311 1708 stevel 1312 1708 stevel if (ret != NTWDT_SUCCESS) { 1313 1708 stevel retval = EIO; 1314 1708 stevel break; 1315 1708 stevel } 1316 1708 stevel 1317 1708 stevel NTWDT_DBG(WDT_DBG_IOCTL, ("NTWDTIOCSTATE:" 1318 1708 stevel " timeout/action: %d/%d", timeout, action)); 1319 1708 stevel 1320 1708 stevel ntwdt_data.ntwdt_wd1 = (uint32_t)timeout; 1321 1708 stevel ntwdt_data.ntwdt_wd2 = (uint8_t)action; 1322 1708 stevel 1323 1708 stevel if (ddi_copyout((caddr_t)&ntwdt_data, (caddr_t)arg, 1324 1708 stevel sizeof (ntwdt_data_t), mode) != 0) { 1325 1708 stevel retval = EFAULT; 1326 1708 stevel } 1327 1708 stevel break; 1328 1708 stevel } 1329 1708 stevel #endif 1330 1708 stevel default: 1331 1708 stevel retval = EINVAL; 1332 1708 stevel break; 1333 1708 stevel } 1334 1708 stevel 1335 1708 stevel return (retval); 1336 1708 stevel end: 1337 1708 stevel mutex_exit(&wdog_state->ntwdt_wdog_mutex); 1338 1708 stevel return (retval); 1339 1708 stevel } 1340 1708 stevel 1341 1708 stevel /* 1342 1708 stevel * Arm the Virtual Watchdog Timer (VWDT). 1343 1708 stevel * 1344 1708 stevel * Assign the current watchdog timeout (ntwdt_wdog_timeout) 1345 1708 stevel * to the softstate variable representing the watchdog 1346 1708 stevel * timer (ntwdt_secs_remaining). 1347 1708 stevel * 1348 1708 stevel * To ensure (from ntwdt's perspective) that any actual 1349 1708 stevel * timeout expiration is at least as large as the expected 1350 1708 stevel * timeout, conditionally set/clear a bit that will be 1351 1708 stevel * checked in the Cyclic's softint. 1352 1708 stevel * 1353 1708 stevel * If the Cyclic has been started, the goal is to ignore 1354 1708 stevel * the _next_ firing of the Cyclic, as that firing will 1355 1708 stevel * NOT represent a full, one-second period. If the Cyclic 1356 1708 stevel * has NOT been started yet, then do not ignore the next 1357 1708 stevel * Cyclic's firing, as that's the First One, and it was 1358 1708 stevel * programmed to fire at a specific time (see ntwdt_start_timer). 1359 1708 stevel */ 1360 1708 stevel static void 1361 1708 stevel ntwdt_arm_vwdt(ntwdt_wdog_t *wdog_state) 1362 1708 stevel { 1363 1708 stevel /* arm the watchdog timer (VWDT) */ 1364 1708 stevel wdog_state->ntwdt_secs_remaining = 1365 1708 stevel wdog_state->ntwdt_wdog_timeout; 1366 1708 stevel 1367 1708 stevel if (wdog_state->ntwdt_timer_running != 0) 1368 1708 stevel NTWDT_FLAG_SET(wdog_state, SKIP_CYCLIC); 1369 1708 stevel else 1370 1708 stevel NTWDT_FLAG_CLR(wdog_state, SKIP_CYCLIC); 1371 1708 stevel } 1372 1708 stevel 1373 1708 stevel /* 1374 1708 stevel * Switch from SWDT mode to AWDT mode. 1375 1708 stevel */ 1376 1708 stevel _NOTE(ARGSUSED(0)) 1377 1708 stevel static void 1378 1708 stevel ntwdt_swdt_to_awdt(ntwdt_wdog_t *wdog_state) 1379 1708 stevel { 1380 1708 stevel ASSERT(wdog_state->ntwdt_is_initial_enable == 0); 1381 1708 stevel 1382 1708 stevel /* 1383 1708 stevel * Disable SWDT. If SWDT is currently active, 1384 1708 stevel * display a message so user knows that SWDT Mode 1385 1708 stevel * has terminated. 1386 1708 stevel */ 1387 1708 stevel if (watchdog_enable != 0 || 1388 1708 stevel watchdog_activated != 0) 1389 1708 stevel cmn_err(CE_NOTE, "Hardware watchdog disabled"); 1390 1708 stevel watchdog_enable = 0; 1391 1708 stevel watchdog_activated = 0; 1392 1708 stevel 1393 1708 stevel /* "we are in AWDT mode" */ 1394 1708 stevel ntwdt_watchdog_activated = 1; 1395 1708 stevel NTWDT_DBG(WDT_DBG_VWDT, ("AWDT is enabled")); 1396 1708 stevel } 1397 1708 stevel 1398 1708 stevel /* 1399 1708 stevel * This is the Cyclic that runs at a multiple of the 1400 1708 stevel * AWDT's watchdog-timeout period. This Cyclic runs at 1401 1708 stevel * LOCK_LEVEL (eg, CY_LOCK_LEVEL) and will post a 1402 1708 stevel * soft-interrupt in order to complete all processing. 1403 1708 stevel * 1404 1708 stevel * Executing at LOCK_LEVEL gives this function a high 1405 1708 stevel * interrupt priority, while performing its work via 1406 1708 stevel * a soft-interrupt allows for a consistent (eg, MT-safe) 1407 1708 stevel * view of driver softstate between User and Interrupt 1408 1708 stevel * context. 1409 1708 stevel * 1410 1708 stevel * Context: 1411 1708 stevel * interrupt context: Cyclic framework calls at 1412 1708 stevel * CY_LOCK_LEVEL (=> 10) 1413 1708 stevel */ 1414 1708 stevel _NOTE(ARGSUSED(0)) 1415 1708 stevel static void 1416 1708 stevel ntwdt_cyclic_pat(void *arg) 1417 1708 stevel { 1418 1708 stevel /* post-down to DDI_SOFTINT_LOW */ 1419 1708 stevel ddi_trigger_softintr(ntwdt_cyclic_softint_id); 1420 1708 stevel } 1421 1708 stevel 1422 1708 stevel /* 1423 1708 stevel * This is the soft-interrupt triggered by the AWDT 1424 1708 stevel * Cyclic. 1425 1708 stevel * 1426 1708 stevel * This softint does all the work re: computing whether 1427 1708 stevel * the VWDT expired. It grabs ntwdt_wdog_mutex 1428 1708 stevel * so User Context code (eg, the IOCTLs) cannot run, 1429 1708 stevel * and then it tests whether the VWDT expired. If it 1430 1708 stevel * hasn't, it decrements the VWDT timer by the amount 1431 1708 stevel * of the Cyclic's period. If the timer has expired, 1432 1708 stevel * it initiates Recovery (based on what user specified 1433 1708 stevel * in LOMIOCDOGCTL). 1434 1708 stevel * 1435 1708 stevel * This function also updates the normal system "heartbeat". 1436 1708 stevel * 1437 1708 stevel * Context: 1438 1708 stevel * interrupt-context: DDI_SOFTINT_LOW 1439 1708 stevel */ 1440 1708 stevel static uint_t 1441 1708 stevel ntwdt_cyclic_softint(char *arg) 1442 1708 stevel { 1443 1708 stevel ntwdt_state_t *ntwdt_ptr = (ntwdt_state_t *)arg; 1444 1708 stevel ntwdt_wdog_t *wdog_state; 1445 1708 stevel 1446 1708 stevel wdog_state = ntwdt_ptr->ntwdt_wdog_state; 1447 1708 stevel 1448 1708 stevel mutex_enter(&wdog_state->ntwdt_wdog_mutex); 1449 1708 stevel 1450 1708 stevel if ((wdog_state->ntwdt_wdog_flags & 1451 1708 stevel NTWDT_FLAG_SKIP_CYCLIC) != 0) { 1452 1708 stevel /* 1453 1708 stevel * then skip all processing by this interrupt. 1454 1708 stevel * (see ntwdt_arm_vwdt()). 1455 1708 stevel */ 1456 1708 stevel wdog_state->ntwdt_wdog_flags &= ~NTWDT_FLAG_SKIP_CYCLIC; 1457 1708 stevel goto end; 1458 1708 stevel } 1459 1708 stevel 1460 1708 stevel if (wdog_state->ntwdt_timer_running == 0 || 1461 1708 stevel (ntwdt_ptr->ntwdt_cycl_id == CYCLIC_NONE) || 1462 1708 stevel (wdog_state->ntwdt_wdog_enabled == 0)) 1463 1708 stevel goto end; 1464 1708 stevel 1465 1708 stevel /* re-arm ("pat") the hardware watchdog */ 1466 1708 stevel ntwdt_pat_hw_watchdog(); 1467 1708 stevel 1468 1708 stevel /* Decrement the VWDT and see if it has expired. */ 1469 1708 stevel if (--wdog_state->ntwdt_secs_remaining == 0) { 1470 1708 stevel 1471 1708 stevel cmn_err(CE_WARN, "application-watchdog expired"); 1472 1708 stevel 1473 1708 stevel wdog_state->ntwdt_wdog_expired = 1; 1474 1708 stevel 1475 1708 stevel if (wdog_state->ntwdt_reset_enabled != 0) { 1476 1708 stevel /* 1477 1708 stevel * Update ScApp so that the new wdog-timeout 1478 1708 stevel * value is as specified in the 1479 1708 stevel * NTWDT_BOOT_TIMEOUT_PROP driver Property. 1480 1708 stevel * This timeout is assumedly larger than the 1481 1708 stevel * actual Solaris reboot time. This will allow 1482 1708 stevel * our forced-reboot to not cause an unplanned 1483 1708 stevel * (series of) watchdog expiration(s). 1484 1708 stevel */ 1485 1708 stevel if (ntwdt_disable_timeout_action == 0) 1486 1708 stevel ntwdt_reprogram_wd(ntwdt_ptr); 1487 1708 stevel 1488 1708 stevel mutex_exit(&wdog_state->ntwdt_wdog_mutex); 1489 1708 stevel 1490 1708 stevel NTWDT_DBG(WDT_DBG_VWDT, ("recovery being done")); 1491 1708 stevel 1492 1708 stevel ntwdt_enforce_timeout(); 1493 1708 stevel } else { 1494 1708 stevel NTWDT_DBG(WDT_DBG_VWDT, ("no recovery being done")); 1495 1708 stevel 1496 1708 stevel wdog_state->ntwdt_wdog_enabled = 0; 1497 1708 stevel 1498 1708 stevel /* 1499 1708 stevel * Tell ScApp to disable wdog; this prevents 1500 1708 stevel * the "2x-timeout" artifact. Eg, Solaris 1501 1708 stevel * times-out at t(x) and ScApp times-out at t(2x), 1502 1708 stevel * where (x==ntwdt_wdog_timeout). 1503 1708 stevel */ 1504 1708 stevel (void) ntwdt_set_cfgvar(LW8_WDT_PROP_WDT, 1505 1708 stevel wdog_state->ntwdt_wdog_enabled); 1506 1708 stevel } 1507 1708 stevel 1508 1708 stevel /* Schedule Callout to stop this Cyclic */ 1509 1708 stevel timeout(ntwdt_stop_timer_lock, ntwdt_ptr, 0); 1510 1708 stevel 1511 1708 stevel } else { 1512 1708 stevel _NOTE(EMPTY) 1513 1708 stevel NTWDT_DBG(WDT_DBG_VWDT, ("time remaining in VWDT: %d" 1514 1708 stevel " seconds", wdog_state->ntwdt_secs_remaining)); 1515 1708 stevel } 1516 1708 stevel end: 1517 1708 stevel mutex_exit(&wdog_state->ntwdt_wdog_mutex); 1518 1708 stevel 1519 1708 stevel return (DDI_INTR_CLAIMED); 1520 1708 stevel } 1521 1708 stevel 1522 1708 stevel /* 1523 1708 stevel * Program the AWDT watchdog-timeout value to that specified 1524 1708 stevel * in the NTWDT_BOOT_TIMEOUT_PROP driver Property. However, 1525 1708 stevel * only do this if the AWDT is in the correct state. 1526 1708 stevel * 1527 1708 stevel * Caller's Context: 1528 1708 stevel * o interrupt context: (from software-interrupt) 1529 1708 stevel * o during a panic 1530 1708 stevel */ 1531 1708 stevel static void 1532 1708 stevel ntwdt_reprogram_wd(ntwdt_state_t *ntwdt_ptr) 1533 1708 stevel { 1534 1708 stevel ntwdt_wdog_t *wdog_state = ntwdt_ptr->ntwdt_wdog_state; 1535 1708 stevel 1536 1708 stevel /* 1537 1708 stevel * Program the AWDT watchdog-timeout value only if the 1538 1708 stevel * watchdog is enabled, the user wants to do recovery, 1539 1708 stevel * ("reset is enabled") and the AWDT timer is currently 1540 1708 stevel * running. 1541 1708 stevel */ 1542 1708 stevel if (wdog_state->ntwdt_wdog_enabled != 0 && 1543 1708 stevel wdog_state->ntwdt_reset_enabled != 0 && 1544 1708 stevel wdog_state->ntwdt_timer_running != 0) { 1545 1708 stevel if (ddi_in_panic() != 0) 1546 1708 stevel ntwdt_set_cfgvar_noreply(LW8_WDT_PROP_TO, 1547 1708 stevel wdog_state->ntwdt_boot_timeout); 1548 1708 stevel else 1549 1708 stevel (void) ntwdt_set_cfgvar(LW8_WDT_PROP_TO, 1550 1708 stevel wdog_state->ntwdt_boot_timeout); 1551 1708 stevel } 1552 1708 stevel } 1553 1708 stevel 1554 1708 stevel /* 1555 1708 stevel * This is the callback that was registered to run during a panic. 1556 1708 stevel * It will set the watchdog-timeout value to be that as specified 1557 1708 stevel * in the NTWDT_BOOT_TIMEOUT_PROP driver Property. 1558 1708 stevel * 1559 1708 stevel * Note that unless this Property's value specifies a timeout 1560 1708 stevel * that's larger than the actual reboot latency, ScApp will 1561 1708 stevel * experience a timeout and initiate Recovery. 1562 1708 stevel */ 1563 1708 stevel _NOTE(ARGSUSED(1)) 1564 1708 stevel static boolean_t 1565 1708 stevel ntwdt_panic_cb(void *arg, int code) 1566 1708 stevel { 1567 1708 stevel ASSERT(ddi_in_panic() != 0); 1568 1708 stevel 1569 1708 stevel ntwdt_reprogram_wd((ntwdt_state_t *)arg); 1570 1708 stevel 1571 1708 stevel return (B_TRUE); 1572 1708 stevel } 1573 1708 stevel 1574 1708 stevel /* 1575 1708 stevel * Initialize the Cyclic that is used to monitor the VWDT. 1576 1708 stevel */ 1577 1708 stevel static void 1578 1708 stevel ntwdt_start_timer(ntwdt_state_t *ntwdt_ptr) 1579 1708 stevel { 1580 1708 stevel ntwdt_wdog_t *wdog_state = ntwdt_ptr->ntwdt_wdog_state; 1581 1708 stevel cyc_handler_t *hdlr = &wdog_state->ntwdt_cycl_hdlr; 1582 1708 stevel cyc_time_t *when = &wdog_state->ntwdt_cycl_time; 1583 1708 stevel 1584 1708 stevel /* 1585 1708 stevel * Init Cyclic so its first expiry occurs wdog-timeout 1586 1708 stevel * seconds from the current, absolute time. 1587 1708 stevel */ 1588 1708 stevel when->cyt_interval = wdog_state->ntwdt_cyclic_interval; 1589 1708 stevel when->cyt_when = gethrtime() + when->cyt_interval; 1590 1708 stevel 1591 1708 stevel wdog_state->ntwdt_wdog_expired = 0; 1592 1708 stevel wdog_state->ntwdt_timer_running = 1; 1593 1708 stevel 1594 1708 stevel mutex_enter(&cpu_lock); 1595 1708 stevel if (ntwdt_ptr->ntwdt_cycl_id == CYCLIC_NONE) 1596 1708 stevel ntwdt_ptr->ntwdt_cycl_id = cyclic_add(hdlr, when); 1597 1708 stevel mutex_exit(&cpu_lock); 1598 1708 stevel 1599 1708 stevel NTWDT_DBG(WDT_DBG_VWDT, ("AWDT's cyclic-driven timer is started")); 1600 1708 stevel } 1601 1708 stevel 1602 1708 stevel /* 1603 1708 stevel * Stop the cyclic that is used to monitor the VWDT (and 1604 1708 stevel * was Started by ntwdt_start_timer). 1605 1708 stevel * 1606 1708 stevel * Context: per the Cyclic API, cyclic_remove cannot be called 1607 1708 stevel * from interrupt-context. Note that when this is 1608 1708 stevel * called via a Callout, it's called from base level. 1609 1708 stevel */ 1610 1708 stevel static void 1611 1708 stevel ntwdt_stop_timer(void *arg) 1612 1708 stevel { 1613 1708 stevel ntwdt_state_t *ntwdt_ptr = (void *)arg; 1614 1708 stevel ntwdt_wdog_t *wdog_state = ntwdt_ptr->ntwdt_wdog_state; 1615 1708 stevel 1616 1708 stevel mutex_enter(&cpu_lock); 1617 1708 stevel if (ntwdt_ptr->ntwdt_cycl_id != CYCLIC_NONE) 1618 1708 stevel cyclic_remove(ntwdt_ptr->ntwdt_cycl_id); 1619 1708 stevel mutex_exit(&cpu_lock); 1620 1708 stevel 1621 1708 stevel wdog_state->ntwdt_timer_running = 0; 1622 1708 stevel ntwdt_ptr->ntwdt_cycl_id = CYCLIC_NONE; 1623 1708 stevel 1624 1708 stevel NTWDT_DBG(WDT_DBG_VWDT, ("AWDT's cyclic-driven timer is stopped")); 1625 1708 stevel } 1626 1708 stevel 1627 1708 stevel /* 1628 1708 stevel * Stop the cyclic that is used to monitor the VWDT (and 1629 1708 stevel * do it in a thread-safe manner). 1630 1708 stevel * 1631 1708 stevel * This is a wrapper function for the core function, 1632 1708 stevel * ntwdt_stop_timer. Both functions are useful, as some 1633 1708 stevel * callers will already have the appropriate mutex locked, and 1634 1708 stevel * other callers will not. 1635 1708 stevel */ 1636 1708 stevel static void 1637 1708 stevel ntwdt_stop_timer_lock(void *arg) 1638 1708 stevel { 1639 1708 stevel ntwdt_state_t *ntwdt_ptr = (void *)arg; 1640 1708 stevel ntwdt_wdog_t *wdog_state = ntwdt_ptr->ntwdt_wdog_state; 1641 1708 stevel 1642 1708 stevel mutex_enter(&wdog_state->ntwdt_wdog_mutex); 1643 1708 stevel ntwdt_stop_timer(arg); 1644 1708 stevel mutex_exit(&wdog_state->ntwdt_wdog_mutex); 1645 1708 stevel } 1646 1708 stevel 1647 1708 stevel /* 1648 1708 stevel * Add callbacks needed to react to major system state transitions. 1649 1708 stevel */ 1650 1708 stevel static void 1651 1708 stevel ntwdt_add_callbacks(ntwdt_state_t *ntwdt_ptr) 1652 1708 stevel { 1653 1708 stevel /* register a callback that's called during a panic */ 1654 1708 stevel ntwdt_callback_ids.ntwdt_panic_cb = callb_add(ntwdt_panic_cb, 1655 1708 stevel (void *)ntwdt_ptr, CB_CL_PANIC, "ntwdt_panic_cb"); 1656 1708 stevel } 1657 1708 stevel 1658 1708 stevel /* 1659 1708 stevel * Remove callbacks added by ntwdt_add_callbacks. 1660 1708 stevel */ 1661 1708 stevel static void 1662 1708 stevel ntwdt_remove_callbacks() 1663 1708 stevel { 1664 1708 stevel callb_delete(ntwdt_callback_ids.ntwdt_panic_cb); 1665 1708 stevel } 1666 1708 stevel 1667 1708 stevel /* 1668 1708 stevel * Initiate a Reset (as a result of the VWDT timeout expiring). 1669 1708 stevel */ 1670 1708 stevel static void 1671 1708 stevel ntwdt_enforce_timeout() 1672 1708 stevel { 1673 1708 stevel if (ntwdt_disable_timeout_action != 0) { 1674 1708 stevel cmn_err(CE_NOTE, "OS timeout expired, taking no action"); 1675 1708 stevel return; 1676 1708 stevel } 1677 1708 stevel 1678 1708 stevel NTWDT_DBG(WDT_DBG_VWDT, ("VWDT expired; do a crashdump")); 1679 1708 stevel 1680 1708 stevel (void) kadmin(A_DUMP, AD_BOOT, NULL, kcred); 1681 1708 stevel cmn_err(CE_PANIC, "kadmin(A_DUMP, AD_BOOT) failed"); 1682 1708 stevel _NOTE(NOTREACHED) 1683 1708 stevel } 1684 1708 stevel 1685 1708 stevel /* 1686 1708 stevel * Interpret the Properties from driver's config file. 1687 1708 stevel */ 1688 1708 stevel static int 1689 1708 stevel ntwdt_read_props(ntwdt_state_t *ntwdt_ptr) 1690 1708 stevel { 1691 1708 stevel ntwdt_wdog_t *wdog_state; 1692 1708 stevel int boot_timeout; 1693 1708 stevel 1694 1708 stevel wdog_state = ntwdt_ptr->ntwdt_wdog_state; 1695 1708 stevel 1696 1708 stevel /* 1697 1708 stevel * interpret Property that specifies how long 1698 1708 stevel * the watchdog-timeout should be set to when 1699 1708 stevel * Solaris panics. Assumption is that this value 1700 1708 stevel * is larger than the amount of time it takes 1701 1708 stevel * to reboot and write crashdump. If not, 1702 1708 stevel * ScApp could induce a reset, due to an expired 1703 1708 stevel * watchdog-timeout. 1704 1708 stevel */ 1705 1708 stevel wdog_state->ntwdt_boot_timeout = 1706 1708 stevel NTWDT_DEFAULT_BOOT_TIMEOUT; 1707 1708 stevel 1708 1708 stevel boot_timeout = ddi_prop_get_int(DDI_DEV_T_ANY, 1709 1708 stevel ntwdt_ptr->ntwdt_dip, DDI_PROP_DONTPASS, 1710 1708 stevel NTWDT_BOOT_TIMEOUT_PROP, -1); 1711 1708 stevel 1712 1708 stevel if (boot_timeout != -1 && boot_timeout > 0 && 1713 1708 stevel boot_timeout <= NTWDT_MAX_TIMEOUT) { 1714 1708 stevel wdog_state->ntwdt_boot_timeout = 1715 1708 stevel boot_timeout; 1716 1708 stevel } else { 1717 1708 stevel _NOTE(EMPTY) 1718 1708 stevel NTWDT_DBG(WDT_DBG_ENTRY, (NTWDT_BOOT_TIMEOUT_PROP 1719 1708 stevel ": using default of %d seconds.", 1720 1708 stevel wdog_state->ntwdt_boot_timeout)); 1721 1708 stevel } 1722 1708 stevel 1723 1708 stevel return (DDI_SUCCESS); 1724 1708 stevel } 1725 1708 stevel 1726 1708 stevel /* 1727 1708 stevel * Write state of SWDT to ScApp. 1728 1708 stevel * 1729 1708 stevel * Currently, this function is only called on attach() 1730 1708 stevel * of our driver. 1731 1708 stevel * 1732 1708 stevel * Note that we do not need to call this function, eg, 1733 1708 stevel * in response to a solicitation from ScApp (eg, 1734 1708 stevel * the LW8_SC_RESTARTED_EVENT). 1735 1708 stevel * 1736 1708 stevel * Context: 1737 1708 stevel * called in Kernel Context 1738 1708 stevel */ 1739 1708 stevel static int 1740 1708 stevel ntwdt_set_swdt_state() 1741 1708 stevel { 1742 1708 stevel /* 1743 1708 stevel * note that ScApp only needs this one 1744 1708 stevel * variable when system is in SWDT mode. 1745 1708 stevel */ 1746 1708 stevel ntwdt_set_cfgvar(LW8_WDT_PROP_MODE, 1747 1708 stevel LW8_PROP_MODE_SWDT); 1748 1708 stevel 1749 1708 stevel return (0); 1750 1708 stevel } 1751 1708 stevel 1752 1708 stevel /* 1753 1708 stevel * Write all AWDT state to ScApp via the SBBC mailbox 1754 1708 stevel * in IOSRAM. Note that the permutation of Writes 1755 1708 stevel * is as specified in the design spec. 1756 1708 stevel * 1757 1708 stevel * Notes: caller must perform synchronization so that 1758 1708 stevel * this series of Writes is consistent as viewed 1759 1708 stevel * by ScApp (eg, there is no LW8_WDT_xxx mailbox 1760 1708 stevel * command that contains "all Properties"; each 1761 1708 stevel * Property must be written individually). 1762 1708 stevel */ 1763 1708 stevel static int 1764 1708 stevel ntwdt_set_awdt_state(ntwdt_wdog_t *rstatep) 1765 1708 stevel { 1766 1708 stevel /* ScApp expects values in this order: */ 1767 1708 stevel ntwdt_set_cfgvar(LW8_WDT_PROP_MODE, 1768 1708 stevel ntwdt_watchdog_activated != 0); 1769 1708 stevel ntwdt_set_cfgvar(LW8_WDT_PROP_TO, 1770 1708 stevel rstatep->ntwdt_wdog_timeout); 1771 1708 stevel ntwdt_set_cfgvar(LW8_WDT_PROP_RECOV, 1772 1708 stevel rstatep->ntwdt_reset_enabled); 1773 1708 stevel ntwdt_set_cfgvar(LW8_WDT_PROP_WDT, 1774 1708 stevel rstatep->ntwdt_wdog_enabled); 1775 1708 stevel 1776 1708 stevel return (NTWDT_SUCCESS); 1777 1708 stevel } 1778 1708 stevel 1779 1708 stevel /* 1780 1708 stevel * Write a specified WDT Property (and Value) to ScApp. 1781 1708 stevel * 1782 1708 stevel * <Property, Value> is passed in the LW8_MBOX_WDT_SET 1783 1708 stevel * (SBBC) mailbox message. The SBBC mailbox resides in 1784 1708 stevel * IOSRAM. 1785 1708 stevel * 1786 1708 stevel * Note that this function is responsible for ensuring that 1787 1708 stevel * a driver-specific representation of a mailbox <Value> is 1788 1708 stevel * mapped into the representation that is expected by ScApp 1789 1708 stevel * (eg, see LW8_WDT_PROP_RECOV). 1790 1708 stevel */ 1791 1708 stevel static int 1792 1708 stevel ntwdt_set_cfgvar(int var, int val) 1793 1708 stevel { 1794 1708 stevel int rv; 1795 1708 stevel int mbox_val; 1796 1708 stevel lw8_set_wdt_t set_wdt; 1797 1708 stevel 1798 1708 stevel switch (var) { 1799 1708 stevel case LW8_WDT_PROP_RECOV: 1800 1708 stevel #ifdef DEBUG 1801 1708 stevel NTWDT_DBG(WDT_DBG_PROT, ("MBOX_SET of 'recovery-enabled':" 1802 1708 stevel " %s (%d)", (val != 0) ? "enabled" : "disabled", val)); 1803 1708 stevel #endif 1804 1708 stevel mbox_val = (val != 0) ? LW8_PROP_RECOV_ENABLED : 1805 1708 stevel LW8_PROP_RECOV_DISABLED; 1806 1708 stevel break; 1807 1708 stevel 1808 1708 stevel case LW8_WDT_PROP_WDT: 1809 1708 stevel #ifdef DEBUG 1810 1708 stevel NTWDT_DBG(WDT_DBG_PROT, ("MBOX_SET of 'wdog-enabled':" 1811 1708 stevel " %s (%d)", (val != 0) ? "enabled" : "disabled", val)); 1812 1708 stevel #endif 1813 1708 stevel mbox_val = (val != 0) ? LW8_PROP_WDT_ENABLED : 1814 1708 stevel LW8_PROP_WDT_DISABLED; 1815 1708 stevel break; 1816 1708 stevel 1817 1708 stevel case LW8_WDT_PROP_TO: 1818 1708 stevel #ifdef DEBUG 1819 1708 stevel NTWDT_DBG(WDT_DBG_PROT, ("MBOX_SET of 'wdog-timeout':" 1820 1708 stevel " %d seconds", val)); 1821 1708 stevel #endif 1822 1708 stevel mbox_val = val; 1823 1708 stevel break; 1824 1708 stevel 1825 1708 stevel case LW8_WDT_PROP_MODE: 1826 1708 stevel #ifdef DEBUG 1827 1708 stevel NTWDT_DBG(WDT_DBG_PROT, ("MBOX_SET of 'wdog-mode':" 1828 1708 stevel " %s (%d)", (val != LW8_PROP_MODE_SWDT) ? 1829 1708 stevel "AWDT" : "SWDT", val)); 1830 1708 stevel #endif 1831 1708 stevel mbox_val = val; 1832 1708 stevel break; 1833 1708 stevel 1834 1708 stevel default: 1835 1708 stevel ASSERT(0); 1836 1708 stevel _NOTE(NOTREACHED) 1837 1708 stevel } 1838 1708 stevel 1839 1708 stevel set_wdt.property_id = var; 1840 1708 stevel set_wdt.value = mbox_val; 1841 1708 stevel 1842 1708 stevel rv = ntwdt_lomcmd(LW8_MBOX_WDT_SET, (intptr_t)&set_wdt); 1843 1708 stevel if (rv != 0) { 1844 1708 stevel _NOTE(EMPTY) 1845 1708 stevel NTWDT_DBG(WDT_DBG_PROT, ("MBOX_SET of prop/val %d/%d " 1846 1708 stevel "failed: %d", var, mbox_val, rv)); 1847 1708 stevel } 1848 1708 stevel 1849 1708 stevel return (rv); 1850 1708 stevel } 1851 1708 stevel 1852 1708 stevel static void 1853 1708 stevel ntwdt_set_cfgvar_noreply(int var, int val) 1854 1708 stevel { 1855 1708 stevel ntwdt_set_cfgvar(var, val); 1856 1708 stevel } 1857 1708 stevel 1858 1708 stevel #ifdef DEBUG 1859 1708 stevel /* 1860 1708 stevel * Read a specified WDT Property from ScApp. 1861 1708 stevel * 1862 1708 stevel * <Property> is passed in the Request of the LW8_MBOX_WDT_GET 1863 1708 stevel * (SBBC) mailbox message, and the Property's <Value> 1864 1708 stevel * is returned in the message's Response. The SBBC mailbox 1865 1708 stevel * resides in IOSRAM. 1866 1708 stevel */ 1867 1708 stevel static int 1868 1708 stevel ntwdt_get_cfgvar(int var, int *val) 1869 1708 stevel { 1870 1708 stevel lw8_get_wdt_t get_wdt; 1871 1708 stevel int rv; 1872 1708 stevel 1873 1708 stevel rv = ntwdt_lomcmd(LW8_MBOX_WDT_GET, (intptr_t)&get_wdt); 1874 1708 stevel if (rv != 0) { 1875 1708 stevel _NOTE(EMPTY) 1876 1708 stevel NTWDT_DBG(WDT_DBG_PROT, ("MBOX_GET failed: %d", rv)); 1877 1708 stevel } else { 1878 1708 stevel switch (var) { 1879 1708 stevel case LW8_WDT_PROP_RECOV: 1880 1708 stevel *val = (uint8_t)get_wdt.recovery_enabled; 1881 1708 stevel NTWDT_DBG(WDT_DBG_PROT, ("MBOX_GET of 'reset-enabled':" 1882 1708 stevel " %s (%d)", (*val != 0) ? "enabled" : "disabled", 1883 1708 stevel *val)); 1884 1708 stevel break; 1885 1708 stevel 1886 1708 stevel case LW8_WDT_PROP_WDT: 1887 1708 stevel *val = (uint8_t)get_wdt.watchdog_enabled; 1888 1708 stevel NTWDT_DBG(WDT_DBG_PROT, ("MBOX_GET of 'wdog-enabled':" 1889 1708 stevel " %s (%d)", (*val != 0) ? "enabled" : "disabled", 1890 1708 stevel *val)); 1891 1708 stevel break; 1892 1708 stevel 1893 1708 stevel case LW8_WDT_PROP_TO: 1894 1708 stevel *val = (uint8_t)get_wdt.timeout; 1895 1708 stevel NTWDT_DBG(WDT_DBG_PROT, ("MBOX_GET of 'wdog-timeout':" 1896 1708 stevel " %d seconds", *val)); 1897 1708 stevel break; 1898 1708 stevel 1899 1708 stevel default: 1900 1708 stevel ASSERT(0); 1901 1708 stevel _NOTE(NOTREACHED) 1902 1708 stevel } 1903 1708 stevel } 1904 1708 stevel 1905 1708 stevel return (rv); 1906 1708 stevel } 1907 1708 stevel #endif 1908 1708 stevel 1909 1708 stevel /* 1910 1708 stevel * Update the real system "heartbeat", which resides in IOSRAM. 1911 1708 stevel * This "heartbeat" is normally used in SWDT Mode, but when 1912 1708 stevel * in AWDT Mode, ScApp also uses its value to determine if Solaris 1913 1708 stevel * is up-and-running. 1914 1708 stevel */ 1915 1708 stevel static void 1916 1708 stevel ntwdt_pat_hw_watchdog() 1917 1708 stevel { 1918 1708 stevel tod_iosram_t tod_buf; 1919 1708 stevel static uint32_t i_am_alive = 0; 1920 1708 stevel #ifdef DEBUG 1921 1708 stevel if (ntwdt_stop_heart != 0) 1922 1708 stevel return; 1923 1708 stevel #endif 1924 1708 stevel /* Update the system heartbeat */ 1925 1708 stevel if (i_am_alive == UINT32_MAX) 1926 1708 stevel i_am_alive = 0; 1927 1708 stevel else 1928 1708 stevel i_am_alive++; 1929 1708 stevel 1930 1708 stevel NTWDT_DBG(WDT_DBG_HEART, ("update heartbeat: %d", 1931 1708 stevel i_am_alive)); 1932 1708 stevel 1933 1708 stevel if (iosram_write(SBBC_TOD_KEY, OFFSET(tod_buf, tod_i_am_alive), 1934 7799 Richard (char *)&i_am_alive, sizeof (uint32_t))) { 1935 1708 stevel cmn_err(CE_WARN, "ntwdt_pat_hw_watchdog(): " 1936 1708 stevel "write heartbeat failed"); 1937 1708 stevel } 1938 1708 stevel } 1939 1708 stevel 1940 1708 stevel /* 1941 1708 stevel * Write the specified value to the system's normal (IOSRAM) 1942 1708 stevel * location that's used to specify Solaris' watchdog-timeout 1943 1708 stevel * on Serengeti platforms. 1944 1708 stevel * 1945 1708 stevel * In SWDT Mode, this location can hold values [0,n). 1946 1708 stevel * In AWDT Mode, this location must have value 0 (else 1947 1708 stevel * after a ScApp-reboot, ScApp could mistakenly interpret 1948 1708 stevel * that the system is in SWDT Mode). 1949 1708 stevel */ 1950 1708 stevel static int 1951 1708 stevel ntwdt_set_hw_timeout(uint32_t period) 1952 1708 stevel { 1953 1708 stevel tod_iosram_t tod_buf; 1954 1708 stevel int rv; 1955 1708 stevel 1956 1708 stevel rv = iosram_write(SBBC_TOD_KEY, OFFSET(tod_buf, tod_timeout_period), 1957 1708 stevel (char *)&period, sizeof (uint32_t)); 1958 1708 stevel if (rv != 0) 1959 1708 stevel cmn_err(CE_WARN, "write of %d for TOD timeout " 1960 1708 stevel "period failed: %d", period, rv); 1961 1708 stevel 1962 1708 stevel return (rv); 1963 1708 stevel } 1964 1708 stevel 1965 1708 stevel /* 1966 1708 stevel * Soft-interrupt handler that is triggered when ScApp wants 1967 1708 stevel * to know the current state of the app-wdog. 1968 1708 stevel * 1969 1708 stevel * Grab ntwdt_wdog_mutex so that we synchronize with any 1970 1708 stevel * concurrent User Context and Interrupt Context activity. Call 1971 1708 stevel * a function that writes a permutation of the watchdog state 1972 1708 stevel * to the SC, then release the mutex. 1973 1708 stevel * 1974 1708 stevel * We grab the mutex not only so that each variable is consistent 1975 1708 stevel * but also so that the *permutation* of variables is consistent. 1976 1708 stevel * I.e., any set of one or more variables (that we write to SC 1977 1708 stevel * using multiple mailbox commands) will truly be seen as a 1978 1708 stevel * consistent snapshot. Note that if our protocol had a MBOX_SET 1979 1708 stevel * command that allowed writing all watchdog state in one 1980 1708 stevel * command, then the lock-hold latency would be greatly reduced. 1981 1708 stevel * To our advantage, this softint normally executes very 1982 1708 stevel * infrequently. 1983 1708 stevel * 1984 1708 stevel * Context: 1985 1708 stevel * called at Interrupt Context (DDI_SOFTINT_LOW) 1986 1708 stevel */ 1987 1708 stevel static uint_t 1988 1708 stevel ntwdt_mbox_softint(char *arg) 1989 1708 stevel { 1990 1708 stevel ntwdt_wdog_t *wdog_state; 1991 1708 stevel 1992 1708 stevel wdog_state = ((ntwdt_state_t *)arg)->ntwdt_wdog_state; 1993 1708 stevel 1994 1708 stevel ASSERT(wdog_state != NULL); 1995 1708 stevel 1996 1708 stevel mutex_enter(&wdog_state->ntwdt_wdog_mutex); 1997 1708 stevel 1998 1708 stevel /* tell ScApp state of AWDT */ 1999 1708 stevel ntwdt_set_awdt_state(wdog_state); 2000 1708 stevel 2001 1708 stevel mutex_exit(&wdog_state->ntwdt_wdog_mutex); 2002 1708 stevel 2003 1708 stevel return (DDI_INTR_CLAIMED); 2004 1708 stevel } 2005 1708 stevel 2006 1708 stevel /* 2007 1708 stevel * Handle MBOX_EVENT_LW8 Events that are sent from ScApp. 2008 1708 stevel * 2009 1708 stevel * The only (sub-)type of Event we handle is the 2010 1708 stevel * LW8_EVENT_SC_RESTARTED Event. We handle this by triggering 2011 1708 stevel * a soft-interrupt only if we are in AWDT mode. 2012 1708 stevel * 2013 1708 stevel * ScApp sends this Event when it wants to learn the current 2014 1708 stevel * state of the AWDT variables. Design-wise, this is used to 2015 1708 stevel * handle the case where the SC reboots while the system is in 2016 1708 stevel * AWDT mode (if the SC reboots in SWDT mode, then ScApp 2017 1708 stevel * already knows all necessary info and therefore won't send 2018 1708 stevel * this Event). 2019 1708 stevel * 2020 1708 stevel * Context: 2021 1708 stevel * function is called in Interrupt Context (at DDI_SOFTINT_MED) 2022 1708 stevel * and we conditionally trigger a softint that will run at 2023 1708 stevel * DDI_SOFTINT_LOW. Note that function executes at 2024 1708 stevel * DDI_SOFTINT_MED due to how this handler was registered by 2025 1708 stevel * the implementation of sbbc_mbox_reg_intr(). 2026 1708 stevel * 2027 1708 stevel * Notes: 2028 1708 stevel * Currently, the LW8_EVENT_SC_RESTARTED Event is only sent 2029 1708 stevel * by SC when in AWDT mode. 2030 1708 stevel */ 2031 1708 stevel static uint_t 2032 1708 stevel ntwdt_event_data_handler(char *arg) 2033 1708 stevel { 2034 1708 stevel lw8_event_t *payload; 2035 1708 stevel sbbc_msg_t *msg; 2036 1708 stevel 2037 1708 stevel if (arg == NULL) { 2038 1708 stevel return (DDI_INTR_CLAIMED); 2039 1708 stevel } 2040 1708 stevel 2041 1708 stevel msg = (sbbc_msg_t *)arg; 2042 1708 stevel if (msg->msg_buf == NULL) { 2043 1708 stevel return (DDI_INTR_CLAIMED); 2044 1708 stevel } 2045 1708 stevel 2046 1708 stevel payload = (lw8_event_t *)msg->msg_buf; 2047 1708 stevel 2048 1708 stevel switch (payload->event_type) { 2049 1708 stevel case LW8_EVENT_SC_RESTARTED: 2050 1708 stevel /* 2051 1708 stevel * then SC probably was rebooted, and it therefore 2052 1708 stevel * needs to know what the current state of AWDT is. 2053 1708 stevel */ 2054 1708 stevel NTWDT_DBG(WDT_DBG_EVENT, ("LW8_EVENT_SC_RESTARTED " 2055 1708 stevel "received in %s mode", 2056 1708 stevel (ntwdt_watchdog_activated != 0) ? "AWDT" : "SWDT")); 2057 1708 stevel 2058 1708 stevel if (ntwdt_watchdog_activated != 0) { 2059 1708 stevel /* then system is in AWDT mode */ 2060 1708 stevel ddi_trigger_softintr(ntwdt_mbox_softint_id); 2061 1708 stevel } 2062 1708 stevel break; 2063 1708 stevel 2064 1708 stevel default: 2065 1708 stevel NTWDT_DBG(WDT_DBG_EVENT, 2066 1708 stevel ("MBOX_EVENT_LW8: %d", payload->event_type)); 2067 1708 stevel break; 2068 1708 stevel } 2069 1708 stevel 2070 1708 stevel return (DDI_INTR_CLAIMED); 2071 1708 stevel } 2072 1708 stevel 2073 1708 stevel /* 2074 1708 stevel * Send an SBBC Mailbox command to ScApp. 2075 1708 stevel * 2076 1708 stevel * Use the sbbc_mbox_request_response utility function to 2077 1708 stevel * send the Request and receive the optional Response. 2078 1708 stevel * 2079 1708 stevel * Context: 2080 1708 stevel * can be called from Interrupt Context or User Context. 2081 1708 stevel */ 2082 1708 stevel static int 2083 1708 stevel ntwdt_lomcmd(int cmd, intptr_t arg) 2084 1708 stevel { 2085 1708 stevel sbbc_msg_t request; 2086 1708 stevel sbbc_msg_t *reqp; 2087 1708 stevel sbbc_msg_t response; 2088 1708 stevel sbbc_msg_t *resp; 2089 1708 stevel int rv = 0; 2090 1708 stevel 2091 1708 stevel reqp = &request; 2092 1708 stevel bzero((caddr_t)&request, sizeof (request)); 2093 1708 stevel reqp->msg_type.type = LW8_MBOX; 2094 1708 stevel reqp->msg_type.sub_type = (uint16_t)cmd; 2095 1708 stevel 2096 1708 stevel resp = &response; 2097 1708 stevel bzero((caddr_t)&response, sizeof (response)); 2098 1708 stevel resp->msg_type.type = LW8_MBOX; 2099 1708 stevel resp->msg_type.sub_type = (uint16_t)cmd; 2100 1708 stevel 2101 1708 stevel switch (cmd) { 2102 1708 stevel case LW8_MBOX_WDT_GET: 2103 1708 stevel reqp->msg_len = 0; 2104 1708 stevel reqp->msg_buf = (caddr_t)NULL; 2105 1708 stevel resp->msg_len = sizeof (lw8_get_wdt_t); 2106 1708 stevel resp->msg_buf = (caddr_t)arg; 2107 1708 stevel break; 2108 1708 stevel 2109 1708 stevel case LW8_MBOX_WDT_SET: 2110 1708 stevel reqp->msg_len = sizeof (lw8_set_wdt_t); 2111 1708 stevel reqp->msg_buf = (caddr_t)arg; 2112 1708 stevel resp->msg_len = 0; 2113 1708 stevel resp->msg_buf = (caddr_t)NULL; 2114 1708 stevel break; 2115 1708 stevel 2116 1708 stevel default: 2117 1708 stevel return (EINVAL); 2118 1708 stevel } 2119 1708 stevel 2120 1708 stevel rv = sbbc_mbox_request_response(reqp, resp, 2121 7799 Richard LW8_DEFAULT_MAX_MBOX_WAIT_TIME); 2122 1708 stevel 2123 1708 stevel if ((rv) || (resp->msg_status != SG_MBOX_STATUS_SUCCESS)) { 2124 1708 stevel 2125 1708 stevel NTWDT_NDBG(WDT_DBG_PROT, ("SBBC mailbox error:" 2126 1708 stevel " (rv/msg_status)=(%d/%d)", rv, resp->msg_status)); 2127 1708 stevel 2128 1708 stevel /* errors from sgsbbc */ 2129 1708 stevel if (resp->msg_status > 0) { 2130 1708 stevel return (resp->msg_status); 2131 1708 stevel } 2132 1708 stevel 2133 1708 stevel /* errors from ScApp */ 2134 1708 stevel switch (resp->msg_status) { 2135 1708 stevel case SG_MBOX_STATUS_ILLEGAL_PARAMETER: 2136 1708 stevel /* illegal ioctl parameter */ 2137 1708 stevel return (EINVAL); 2138 1708 stevel 2139 1708 stevel default: 2140 1708 stevel return (EIO); 2141 1708 stevel } 2142 1708 stevel } 2143 1708 stevel return (0); 2144 1708 stevel } 2145