103831d35Sstevel /* 203831d35Sstevel * CDDL HEADER START 303831d35Sstevel * 403831d35Sstevel * The contents of this file are subject to the terms of the 503831d35Sstevel * Common Development and Distribution License (the "License"). 603831d35Sstevel * You may not use this file except in compliance with the License. 703831d35Sstevel * 803831d35Sstevel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 903831d35Sstevel * or http://www.opensolaris.org/os/licensing. 1003831d35Sstevel * See the License for the specific language governing permissions 1103831d35Sstevel * and limitations under the License. 1203831d35Sstevel * 1303831d35Sstevel * When distributing Covered Code, include this CDDL HEADER in each 1403831d35Sstevel * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 1503831d35Sstevel * If applicable, add the following below this CDDL HEADER, with the 1603831d35Sstevel * fields enclosed by brackets "[]" replaced with your own identifying 1703831d35Sstevel * information: Portions Copyright [yyyy] [name of copyright owner] 1803831d35Sstevel * 1903831d35Sstevel * CDDL HEADER END 2003831d35Sstevel */ 2103831d35Sstevel 2203831d35Sstevel /* 23*07d06da5SSurya Prakki * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 2403831d35Sstevel * Use is subject to license terms. 2503831d35Sstevel */ 2603831d35Sstevel 2703831d35Sstevel /* 2803831d35Sstevel * ntwdt driver 2903831d35Sstevel * ------------ 3003831d35Sstevel * 3103831d35Sstevel * Subsystem Overview 3203831d35Sstevel * ------------------ 3303831d35Sstevel * 3403831d35Sstevel * This is a pseudo driver for the Netra-1280 watchdog 3503831d35Sstevel * timer (WDT). It provides for an *application-driven* 3603831d35Sstevel * WDT (AWDT), not a traditional, hardware-based WDT. A 3703831d35Sstevel * hardware-based feature is already present on the 3803831d35Sstevel * Netra-1280, and it is referred to here as the 3903831d35Sstevel * System WDT (SWDT). 4003831d35Sstevel * 4103831d35Sstevel * ScApp and Solaris cooperate to provide either a SWDT or 4203831d35Sstevel * an AWDT; they are mutually-exclusive. Once in AWDT 4303831d35Sstevel * mode, one can only transition to SWDT mode via a reboot. 4403831d35Sstevel * This obviously gives priority to the AWDT and was done 4503831d35Sstevel * to handle scenarios where the customer might temporarily 4603831d35Sstevel * terminate their wdog-app in order to do some debugging, 4703831d35Sstevel * or even to load a new version of the wdog-app. 4803831d35Sstevel * 4903831d35Sstevel * The wdog-app does an open() of the /dev/ntwdt device node 5003831d35Sstevel * and then issues ioctl's to control the state of the AWDT. 5103831d35Sstevel * The ioctl's are implemented by this driver. Only one 5203831d35Sstevel * concurrent instance of open() is allowed. On the close(), 5303831d35Sstevel * a watchdog timer still in progress is NOT terminated. 5403831d35Sstevel * This allows the global state machine to monitor the 5503831d35Sstevel * progress of a Solaris reboot. ScApp will reset Solaris 5603831d35Sstevel * (eg, send an XIR) if the actual boot/crashdump latency 5703831d35Sstevel * is larger than the current AWDT timeout. 5803831d35Sstevel * 5903831d35Sstevel * The rationale for implementing an AWDT (vs a SWDT) is 6003831d35Sstevel * that it is more sensitive to system outage scenarios than 6103831d35Sstevel * a SWDT. Eg, a system could be in such a failed state that 6203831d35Sstevel * even though its clock-interrupt could still run (and the 6303831d35Sstevel * SWDT's watchdog timer therefore re-armed), the system could 6403831d35Sstevel * in effect have a corrupt or very poor dispatch latency. 6503831d35Sstevel * An AWDT would be sensitive to dispatch latency issues, as 6603831d35Sstevel * well as problems with its own execution (eg, a hang or 6703831d35Sstevel * crash). 6803831d35Sstevel * 6903831d35Sstevel * Subsystem Interface Overview 7003831d35Sstevel * ---------------------------- 7103831d35Sstevel * 7203831d35Sstevel * This pseudo-driver does not have any 'extern' functions. 7303831d35Sstevel * 7403831d35Sstevel * All system interaction is done via the traditional driver 7503831d35Sstevel * entry points (eg, attach(9e), _init(9e)). 7603831d35Sstevel * 7703831d35Sstevel * All interaction with user is via the entry points in the 7803831d35Sstevel * 'struct cb_ops' vector (eg, open(9e), ioctl(9e), and 7903831d35Sstevel * close(9e)). 8003831d35Sstevel * 8103831d35Sstevel * Subsystem Implementation Overview 8203831d35Sstevel * --------------------------------- 8303831d35Sstevel * 8403831d35Sstevel * ScApp and Solaris (eg, ntwdt) cooperate so that a state 8503831d35Sstevel * machine global to ScApp and ntwdt is either in AWDT mode 8603831d35Sstevel * or in SWDT mode. These two peers communicate via the SBBC 8703831d35Sstevel * Mailbox that resides in IOSRAM (SBBC_MAILBOX_KEY). 8803831d35Sstevel * They use two new mailbox messages (LW8_MBOX_WDT_GET and 8903831d35Sstevel * LW8_MBOX_WDT_SET) and one new event (LW8_EVENT_SC_RESTARTED). 9003831d35Sstevel * 9103831d35Sstevel * ntwdt implements the AWDT by implementing a "virtual 9203831d35Sstevel * WDT" (VWDT). Eg, the watchdog timer is not a traditional 9303831d35Sstevel * counter in hardware, it is a variable in ntwdt's 9403831d35Sstevel * softstate. The wdog-app's actions cause changes to this 9503831d35Sstevel * and other variables in ntwdt's softstate. 9603831d35Sstevel * 9703831d35Sstevel * The wdog-app uses the LOMIOCDOGTIME ioctl to specify 9803831d35Sstevel * the number of seconds in the watchdog timeout (and 9903831d35Sstevel * therefore the VWDT). The wdog-app then uses the 10003831d35Sstevel * LOMIOCDOGCTL ioctl to enable the wdog. This causes 10103831d35Sstevel * ntwdt to create a Cyclic that will both decrement 10203831d35Sstevel * the VWDT and check to see if it has expired. To keep 10303831d35Sstevel * the VWDT from expiring, the wdog-app uses the 10403831d35Sstevel * LOMIOCDOGPAT ioctl to re-arm (or "pat") the watchdog. 10503831d35Sstevel * This sets the VWDT value to that specified in the 10603831d35Sstevel * last LOMIOCDOGTIME ioctl. The wdog-app can use the 10703831d35Sstevel * LOMIOCDOGSTATE ioctl to query the state of the VWDT. 10803831d35Sstevel * 10903831d35Sstevel * The wdog-app can also specify how Recovery is to be 11003831d35Sstevel * done. The only choice is whether to do a crashdump 11103831d35Sstevel * or not. If ntwdt computes a VWDT expiration, then 11203831d35Sstevel * ntwdt initiates the Recovery, else ScApp will. Eg, 11303831d35Sstevel * a hang in Solaris will be sensed by ScApp and not 11403831d35Sstevel * ntwdt. The wdog-app specifies the Recovery policy 11503831d35Sstevel * via the DOGCTL ioctl. 11603831d35Sstevel * 11703831d35Sstevel * Timeout Expiration 11803831d35Sstevel * ------------------ 11903831d35Sstevel * In our implementation, ScApp senses a watchdog 12003831d35Sstevel * expiration the same way it historically has: 12103831d35Sstevel * by reading a well-known area of IOSRAM (SBBC_TOD_KEY) 12203831d35Sstevel * to see if the timestamp associated with a 12303831d35Sstevel * Solaris-generated "heartbeat" field is older 12403831d35Sstevel * than the currently specified timeout (which is 12503831d35Sstevel * also specified in this same IOSRAM section). 12603831d35Sstevel * 12703831d35Sstevel * What is different when ntwdt is running is that 12803831d35Sstevel * ntwdt is responsible for updating the Heartbeat, 12903831d35Sstevel * and not the normal client (todsg). When ntwdt 13003831d35Sstevel * puts the system in AWDT mode, it disables todsg's 13103831d35Sstevel * updating of the Heartbeat by changing the state of 13203831d35Sstevel * a pair of kernel tunables (watchdog_activated and 13303831d35Sstevel * watchdog_enable). ntwdt then takes responsibility 13403831d35Sstevel * for updating the Heartbeat. It does this by 13503831d35Sstevel * updating the Heartbeat from the Cyclic that is 13603831d35Sstevel * created when the user enables the AWDT (DOGCTL) 13703831d35Sstevel * or specifies a new timeout value (DOGTIME). 13803831d35Sstevel * 13903831d35Sstevel * As long as the AWDT is enabled, ntwdt will update 14003831d35Sstevel * the real system Heartbeat. As a result, ScApp 14103831d35Sstevel * will conclude that Solaris is still running. If 14203831d35Sstevel * the user stops re-arming the VWDT or Solaris 14303831d35Sstevel * hangs (eg), ntwdt will stop updating the Heartbeat. 14403831d35Sstevel * 14503831d35Sstevel * Note that ntwdt computes expiration via the 14603831d35Sstevel * repeatedly firing Cyclic, and ScApp computes 14703831d35Sstevel * expiration via a cessation of Heartbeat update. 14803831d35Sstevel * Since Heartbeat update stops once user stops 14903831d35Sstevel * re-arming the VWDT (ie, DOGPAT ioctl), ntwdt 15003831d35Sstevel * will compute a timeout at t(x), and ScApp will 15103831d35Sstevel * compute a timeout at t(2x), where 'x' is the 15203831d35Sstevel * current timeout value. When ntwdt computes 15303831d35Sstevel * the expiration, ntwdt masks this asymmetry. 15403831d35Sstevel * 15503831d35Sstevel * Lifecycle Events 15603831d35Sstevel * ---------------- 15703831d35Sstevel * 15803831d35Sstevel * ntwdt only handles one of the coarse-grained 15903831d35Sstevel * "lifecycle events" (eg, entering OBP, shutdown, 16003831d35Sstevel * power-down, DR) that are possible during a Solaris 16103831d35Sstevel * session: a panic. (Note that ScApp handles one 16203831d35Sstevel * of the others: "entering OBP"). Other than these, 16303831d35Sstevel * a user choosing such a state transition must first 16403831d35Sstevel * use the wdog-app to disable the watchdog, else 16503831d35Sstevel * an expiration could occur. 16603831d35Sstevel * 16703831d35Sstevel * Solaris handles a panic by registering a handler 16803831d35Sstevel * that's called during the panic. The handler will 16903831d35Sstevel * set the watchdog timeout to the value specified 17003831d35Sstevel * in the NTWDT_BOOT_TIMEOUT_PROP driver Property. 17103831d35Sstevel * Again, this value should be greater than the actual 17203831d35Sstevel * Solaris reboot/crashdump latency. 17303831d35Sstevel * 17403831d35Sstevel * When the user enters OBP via the System Controller, 17503831d35Sstevel * ScApp will disable the watchdog (from ScApp's 17603831d35Sstevel * perspective), but it will not communicate this to 17703831d35Sstevel * ntwdt. After having exited OBP, the wdog-app can 17803831d35Sstevel * be used to enable or disable the watchdog (which 17903831d35Sstevel * will get both ScApp and ntwdt in-sync). 18003831d35Sstevel * 18103831d35Sstevel * Locking 18203831d35Sstevel * ------- 18303831d35Sstevel * 18403831d35Sstevel * ntwdt has code running at three interrupt levels as 18503831d35Sstevel * well as base level. 18603831d35Sstevel * 18703831d35Sstevel * The ioctls run at base level in User Context. The 18803831d35Sstevel * driver's entry points run at base level in Kernel 18903831d35Sstevel * Context. 19003831d35Sstevel * 19103831d35Sstevel * ntwdt's three interrupt levels are used by: 19203831d35Sstevel * 19303831d35Sstevel * o LOCK_LEVEL : 19403831d35Sstevel * the Cyclic used to manage the VWDT is initialized 19503831d35Sstevel * to CY_LOCK_LEVEL 19603831d35Sstevel * 19703831d35Sstevel * o DDI_SOFTINT_MED : 19803831d35Sstevel * the SBBC mailbox implementation registers the 19903831d35Sstevel * specified handlers at this level 20003831d35Sstevel * 20103831d35Sstevel * o DDI_SOFTINT_LOW : 20203831d35Sstevel * this level is used by two handlers. One handler 20303831d35Sstevel * is triggered by the LOCK_LEVEL Cyclic. The other 20403831d35Sstevel * handler is triggered by the DDI_SOFTINT_MED 20503831d35Sstevel * handler registered to handle SBBC mailbox events. 20603831d35Sstevel * 20703831d35Sstevel * The centralizing concept is that the ntwdt_wdog_mutex 20803831d35Sstevel * in the driver's softstate is initialized to have an 20903831d35Sstevel * interrupt-block-cookie corresponding to DDI_SOFTINT_LOW. 21003831d35Sstevel * 21103831d35Sstevel * As a result, any base level code grabs ntwdt_wdog_mutex 21203831d35Sstevel * before doing work. Also, any handler running at interrupt 21303831d35Sstevel * level higher than DDI_SOFTINT_LOW "posts down" so that 21403831d35Sstevel * a DDI_SOFTINT_LOW handler is responsible for executing 21503831d35Sstevel * the "real work". Each DDI_SOFTINT_LOW handler also 21603831d35Sstevel * first grabs ntwdt_wdog_mutex, and so base level is 21703831d35Sstevel * synchronized with all interrupt levels. 21803831d35Sstevel * 21903831d35Sstevel * Note there's another mutex in the softstate: ntwdt_mutex. 22003831d35Sstevel * This mutex has few responsibilities. However, this 22103831d35Sstevel * locking order must be followed: ntwdt_wdog_mutex is 22203831d35Sstevel * held first, and then ntwdt_mutex. This choice results 22303831d35Sstevel * from the fact that the number of dynamic call sites 22403831d35Sstevel * for ntwdt_wdog_mutex is MUCH greater than that of 22503831d35Sstevel * ntwdt_mutex. As a result, almost all uses of 22603831d35Sstevel * ntwdt_wdog_mutex do not even require ntwdt_mutex to 22703831d35Sstevel * be held, which saves resources. 22803831d35Sstevel * 22903831d35Sstevel * Driver Properties 23003831d35Sstevel * ----------------- 23103831d35Sstevel * 23203831d35Sstevel * "ddi-forceattach=1;" 23303831d35Sstevel * ------------------ 23403831d35Sstevel * 23503831d35Sstevel * Using this allows our driver to be automatically 23603831d35Sstevel * loaded at boot-time AND to not be removed from memory 23703831d35Sstevel * solely due to memory-pressure. 23803831d35Sstevel * 23903831d35Sstevel * Being loaded at boot allows ntwdt to (as soon as 24003831d35Sstevel * possible) tell ScApp of the current mode of the 24103831d35Sstevel * state-machine (eg, SWDT). This is needed for the case 24203831d35Sstevel * when Solaris is re-loaded while in AWDT mode; having 24303831d35Sstevel * Solaris communicate ASAP with ScApp reduces the duration 24403831d35Sstevel * of any "split-brain" scenario where ScApp and Solaris 24503831d35Sstevel * are not in the same mode. 24603831d35Sstevel * 24703831d35Sstevel * Having ntwdt remain in memory even after a close() 24803831d35Sstevel * allows ntwdt to answer any SBBC mailbox commands 24903831d35Sstevel * that ScApp sends (as the mailbox infrastructure is 25003831d35Sstevel * not torn down until ntwdt is detach()'d). Specifically, 25103831d35Sstevel * ScApp could be re-loaded after AWDT mode had been 25203831d35Sstevel * entered and the wdog-app had close()'d ntwdt. ScApp 25303831d35Sstevel * will then eventually send a LW8_EVENT_SC_RESTARTED 25403831d35Sstevel * mailbox event in order to learn the current state of 25503831d35Sstevel * state-machine. Having ntwdt remain loaded allows this 25603831d35Sstevel * event to never go unanswered. 25703831d35Sstevel * 25803831d35Sstevel * "ntwdt-boottimeout=600;" 25903831d35Sstevel * ---------------------- 26003831d35Sstevel * 26103831d35Sstevel * This specifies the watchdog timeout value (in seconds) to 26203831d35Sstevel * use when ntwdt is aware of the need to reboot/reload Solaris. 26303831d35Sstevel * 26403831d35Sstevel * ntwdt will update ScApp by setting the watchdog timeout 26503831d35Sstevel * to the specified number of seconds when either a) Solaris 26603831d35Sstevel * panics or b) the VWDT expires. Note that this is only done 26703831d35Sstevel * if the user has chosen to enable Reset. 26803831d35Sstevel * 26903831d35Sstevel * ntwdt boundary-checks the specified value, and if out-of-range, 27003831d35Sstevel * it initializes the watchdog timeout to a default value of 27103831d35Sstevel * NTWDT_DEFAULT_BOOT_TIMEOUT seconds. Note that this is a 27203831d35Sstevel * default value and is not a *minimum* value. The valid range 27303831d35Sstevel * for the watchdog timeout is between one second and 27403831d35Sstevel * NTWDT_MAX_TIMEOUT seconds, inclusive. 27503831d35Sstevel * 27603831d35Sstevel * If ntwdt-boottimeout is set to a value less than an actual 27703831d35Sstevel * Solaris boot's latency, ScApp will reset Solaris during boot. 27803831d35Sstevel * Note that a continuous series of ScApp-induced resets will 27903831d35Sstevel * not occur; ScApp only resets Solaris on the first transition 28003831d35Sstevel * into the watchdog-expired state. 28103831d35Sstevel */ 28203831d35Sstevel 28303831d35Sstevel #include <sys/note.h> 28403831d35Sstevel #include <sys/types.h> 28503831d35Sstevel #include <sys/callb.h> 28603831d35Sstevel #include <sys/stat.h> 28703831d35Sstevel #include <sys/conf.h> 28803831d35Sstevel #include <sys/ddi.h> 28903831d35Sstevel #include <sys/sunddi.h> 29003831d35Sstevel #include <sys/modctl.h> 29103831d35Sstevel #include <sys/ddi_impldefs.h> 29203831d35Sstevel #include <sys/kmem.h> 29303831d35Sstevel #include <sys/devops.h> 29403831d35Sstevel #include <sys/cyclic.h> 29503831d35Sstevel #include <sys/uadmin.h> 29603831d35Sstevel #include <sys/lw8_impl.h> 29703831d35Sstevel #include <sys/sgsbbc.h> 29803831d35Sstevel #include <sys/sgsbbc_iosram.h> 29903831d35Sstevel #include <sys/sgsbbc_mailbox.h> 30003831d35Sstevel #include <sys/todsg.h> 30103831d35Sstevel #include <sys/mem_config.h> 30203831d35Sstevel #include <sys/lom_io.h> 30303831d35Sstevel #include <sys/reboot.h> 30403831d35Sstevel #include <sys/clock.h> 30503831d35Sstevel 30603831d35Sstevel 30703831d35Sstevel /* 30803831d35Sstevel * tunables 30903831d35Sstevel */ 31003831d35Sstevel int ntwdt_disable_timeout_action = 0; 31103831d35Sstevel #ifdef DEBUG 31203831d35Sstevel /* 31303831d35Sstevel * tunable to simulate a Solaris hang. If is non-zero, then 31403831d35Sstevel * no system heartbeats ("hardware patting") will be done, 31503831d35Sstevel * even though all AWDT machinery is functioning OK. 31603831d35Sstevel */ 31703831d35Sstevel int ntwdt_stop_heart; 31803831d35Sstevel #endif 31903831d35Sstevel 32003831d35Sstevel /* 32103831d35Sstevel * Driver Property 32203831d35Sstevel */ 32303831d35Sstevel #define NTWDT_BOOT_TIMEOUT_PROP "ntwdt-boottimeout" 32403831d35Sstevel 32503831d35Sstevel /* 32603831d35Sstevel * watchdog-timeout values (in seconds): 32703831d35Sstevel * 32803831d35Sstevel * NTWDT_DEFAULT_BOOT_TIMEOUT: the default value used if 32903831d35Sstevel * this driver is aware of the 33003831d35Sstevel * reboot. 33103831d35Sstevel * 33203831d35Sstevel * NTWDT_MAX_TIMEOUT: max value settable by app (via the 33303831d35Sstevel * LOMIOCDOGTIME ioctl) 33403831d35Sstevel */ 33503831d35Sstevel #define NTWDT_DEFAULT_BOOT_TIMEOUT (10*60) 33603831d35Sstevel #define NTWDT_MAX_TIMEOUT (180*60) 33703831d35Sstevel 33803831d35Sstevel 33903831d35Sstevel #define NTWDT_CYCLIC_CHK_PERCENT (20) 34003831d35Sstevel #define NTWDT_MINOR_NODE "awdt" 34103831d35Sstevel #define OFFSET(base, field) ((char *)&base.field - (char *)&base) 34203831d35Sstevel 34303831d35Sstevel #define NTWDT_SUCCESS 0 34403831d35Sstevel #define NTWDT_FAILURE 1 34503831d35Sstevel 34603831d35Sstevel typedef struct { 34703831d35Sstevel callb_id_t ntwdt_panic_cb; 34803831d35Sstevel } ntwdt_callback_ids_t; 34903831d35Sstevel static ntwdt_callback_ids_t ntwdt_callback_ids; 35003831d35Sstevel 35103831d35Sstevel /* MBOX_EVENT_LW8 that is sent in IOSRAM Mailbox: */ 35203831d35Sstevel static lw8_event_t lw8_event; /* payload */ 35303831d35Sstevel static sbbc_msg_t sbbc_msg; /* message */ 35403831d35Sstevel 35503831d35Sstevel static ddi_softintr_t ntwdt_mbox_softint_id; 35603831d35Sstevel static ddi_softintr_t ntwdt_cyclic_softint_id; 35703831d35Sstevel 35803831d35Sstevel /* 35903831d35Sstevel * VWDT (i.e., Virtual Watchdog Timer) state 36003831d35Sstevel */ 36103831d35Sstevel typedef struct { 36203831d35Sstevel kmutex_t ntwdt_wdog_mutex; 36303831d35Sstevel ddi_iblock_cookie_t ntwdt_wdog_mtx_cookie; 36403831d35Sstevel int ntwdt_wdog_enabled; /* wdog enabled ? */ 36503831d35Sstevel int ntwdt_reset_enabled; /* reset enabled ? */ 36603831d35Sstevel int ntwdt_timer_running; /* wdog running ? */ 36703831d35Sstevel int ntwdt_wdog_expired; /* wdog expired ? */ 36803831d35Sstevel int ntwdt_is_initial_enable; /* 1st wdog-enable? */ 36903831d35Sstevel uint32_t ntwdt_boot_timeout; /* timeout for boot */ 37003831d35Sstevel uint32_t ntwdt_secs_remaining; /* expiration timer */ 37103831d35Sstevel uint8_t ntwdt_wdog_action; /* Reset action */ 37203831d35Sstevel uint32_t ntwdt_wdog_timeout; /* timeout in seconds */ 37303831d35Sstevel hrtime_t ntwdt_cyclic_interval; /* cyclic interval */ 37403831d35Sstevel cyc_handler_t ntwdt_cycl_hdlr; 37503831d35Sstevel cyc_time_t ntwdt_cycl_time; 37603831d35Sstevel kmutex_t ntwdt_event_lock; /* lock */ 37703831d35Sstevel uint64_t ntwdt_wdog_flags; 37803831d35Sstevel } ntwdt_wdog_t; 37903831d35Sstevel 38003831d35Sstevel /* ntwdt_wdog_flags */ 38103831d35Sstevel #define NTWDT_FLAG_SKIP_CYCLIC 0x1 /* skip next Cyclic */ 38203831d35Sstevel 38303831d35Sstevel /* macros to set/clear one bit in ntwdt_wdog_flags */ 38403831d35Sstevel #define NTWDT_FLAG_SET(p, f)\ 38503831d35Sstevel ((p)->ntwdt_wdog_flags |= NTWDT_FLAG_##f) 38603831d35Sstevel #define NTWDT_FLAG_CLR(p, f)\ 38703831d35Sstevel ((p)->ntwdt_wdog_flags &= ~NTWDT_FLAG_##f) 38803831d35Sstevel 38903831d35Sstevel 39003831d35Sstevel /* softstate */ 39103831d35Sstevel typedef struct { 39203831d35Sstevel kmutex_t ntwdt_mutex; 39303831d35Sstevel dev_info_t *ntwdt_dip; /* dip */ 39403831d35Sstevel int ntwdt_open_flag; /* file open ? */ 39503831d35Sstevel ntwdt_wdog_t *ntwdt_wdog_state; /* wdog state */ 39603831d35Sstevel cyclic_id_t ntwdt_cycl_id; 39703831d35Sstevel } ntwdt_state_t; 39803831d35Sstevel 39903831d35Sstevel static void *ntwdt_statep; /* softstate */ 40003831d35Sstevel static dev_info_t *ntwdt_dip; 40103831d35Sstevel /* 40203831d35Sstevel * if non-zero, then the app-wdog feature is available on 40303831d35Sstevel * this system configuration. 40403831d35Sstevel */ 40503831d35Sstevel static int ntwdt_watchdog_available; 40603831d35Sstevel /* 40703831d35Sstevel * if non-zero, then application has used the LOMIOCDOGCTL 40803831d35Sstevel * ioctl at least once in order to Enable the app-wdog. 40903831d35Sstevel * Also, if this is non-zero, then system is in AWDT mode, 41003831d35Sstevel * else it is in SWDT mode. 41103831d35Sstevel */ 41203831d35Sstevel static int ntwdt_watchdog_activated; 41303831d35Sstevel 41403831d35Sstevel #define getstate(minor) \ 41503831d35Sstevel ((ntwdt_state_t *)ddi_get_soft_state(ntwdt_statep, (minor))) 41603831d35Sstevel 41703831d35Sstevel static int ntwdt_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); 41803831d35Sstevel static int ntwdt_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); 41903831d35Sstevel static int ntwdt_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, 42003831d35Sstevel void **result); 42103831d35Sstevel static int ntwdt_open(dev_t *, int, int, cred_t *); 42203831d35Sstevel static int ntwdt_close(dev_t, int, int, cred_t *); 42303831d35Sstevel static int ntwdt_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 42403831d35Sstevel 42503831d35Sstevel static void ntwdt_reprogram_wd(ntwdt_state_t *); 42603831d35Sstevel static boolean_t ntwdt_panic_cb(void *arg, int code); 42703831d35Sstevel static void ntwdt_start_timer(ntwdt_state_t *); 42803831d35Sstevel static void ntwdt_stop_timer(void *); 42903831d35Sstevel static void ntwdt_stop_timer_lock(void *arg); 43003831d35Sstevel static void ntwdt_add_callbacks(ntwdt_state_t *ntwdt_ptr); 43103831d35Sstevel static void ntwdt_remove_callbacks(); 43203831d35Sstevel static void ntwdt_cyclic_pat(void *arg); 43303831d35Sstevel static void ntwdt_enforce_timeout(); 43403831d35Sstevel static void ntwdt_pat_hw_watchdog(); 43503831d35Sstevel static int ntwdt_set_cfgvar(int var, int val); 43603831d35Sstevel static void ntwdt_set_cfgvar_noreply(int var, int val); 43703831d35Sstevel static int ntwdt_read_props(ntwdt_state_t *); 43803831d35Sstevel static int ntwdt_add_mbox_handlers(ntwdt_state_t *); 43903831d35Sstevel static int ntwdt_set_hw_timeout(uint32_t period); 44003831d35Sstevel static int ntwdt_remove_mbox_handlers(void); 44103831d35Sstevel static uint_t ntwdt_event_data_handler(char *arg); 44203831d35Sstevel static uint_t ntwdt_mbox_softint(char *arg); 44303831d35Sstevel static uint_t ntwdt_cyclic_softint(char *arg); 44403831d35Sstevel static int ntwdt_lomcmd(int cmd, intptr_t arg); 44503831d35Sstevel static int ntwdt_chk_wdog_support(); 44603831d35Sstevel static int ntwdt_chk_sc_support(); 44703831d35Sstevel static int ntwdt_set_swdt_state(); 44803831d35Sstevel static void ntwdt_swdt_to_awdt(ntwdt_wdog_t *); 44903831d35Sstevel static void ntwdt_arm_vwdt(ntwdt_wdog_t *wdog_state); 45003831d35Sstevel #ifdef DEBUG 45103831d35Sstevel static int ntwdt_get_cfgvar(int var, int *val); 45203831d35Sstevel #endif 45303831d35Sstevel 45403831d35Sstevel struct cb_ops ntwdt_cb_ops = { 45503831d35Sstevel ntwdt_open, /* open */ 45603831d35Sstevel ntwdt_close, /* close */ 45703831d35Sstevel nulldev, /* strategy */ 45803831d35Sstevel nulldev, /* print */ 45903831d35Sstevel nulldev, /* dump */ 46003831d35Sstevel nulldev, /* read */ 46103831d35Sstevel nulldev, /* write */ 46203831d35Sstevel ntwdt_ioctl, /* ioctl */ 46303831d35Sstevel nulldev, /* devmap */ 46403831d35Sstevel nulldev, /* mmap */ 46503831d35Sstevel nulldev, /* segmap */ 46603831d35Sstevel nochpoll, /* poll */ 46703831d35Sstevel ddi_prop_op, /* cb_prop_op */ 46803831d35Sstevel NULL, /* streamtab */ 46903831d35Sstevel D_MP | D_NEW 47003831d35Sstevel }; 47103831d35Sstevel 47203831d35Sstevel static struct dev_ops ntwdt_ops = { 47303831d35Sstevel DEVO_REV, /* Devo_rev */ 47403831d35Sstevel 0, /* Refcnt */ 47503831d35Sstevel ntwdt_info, /* Info */ 47603831d35Sstevel nulldev, /* Identify */ 47703831d35Sstevel nulldev, /* Probe */ 47803831d35Sstevel ntwdt_attach, /* Attach */ 47903831d35Sstevel ntwdt_detach, /* Detach */ 48003831d35Sstevel nodev, /* Reset */ 48103831d35Sstevel &ntwdt_cb_ops, /* Driver operations */ 48203831d35Sstevel 0, /* Bus operations */ 48303831d35Sstevel NULL /* Power */ 48403831d35Sstevel }; 48503831d35Sstevel 48603831d35Sstevel static struct modldrv modldrv = { 48703831d35Sstevel &mod_driverops, /* This one is a driver */ 488f500b196SRichard Bean "ntwdt-Netra-T12", /* Name of the module. */ 48903831d35Sstevel &ntwdt_ops, /* Driver ops */ 49003831d35Sstevel }; 49103831d35Sstevel 49203831d35Sstevel static struct modlinkage modlinkage = { 49303831d35Sstevel MODREV_1, (void *)&modldrv, NULL 49403831d35Sstevel }; 49503831d35Sstevel 49603831d35Sstevel 49703831d35Sstevel /* 49803831d35Sstevel * Flags to set in ntwdt_debug. 49903831d35Sstevel * 50003831d35Sstevel * Use either the NTWDT_DBG or NTWDT_NDBG macros 50103831d35Sstevel */ 50203831d35Sstevel #define WDT_DBG_ENTRY 0x00000001 /* drv entry points */ 50303831d35Sstevel #define WDT_DBG_HEART 0x00000002 /* system heartbeat */ 50403831d35Sstevel #define WDT_DBG_VWDT 0x00000004 /* virtual WDT */ 50503831d35Sstevel #define WDT_DBG_EVENT 0x00000010 /* SBBC Mbox events */ 50603831d35Sstevel #define WDT_DBG_PROT 0x00000020 /* SC/Solaris protocol */ 50703831d35Sstevel #define WDT_DBG_IOCTL 0x00000040 /* ioctl's */ 50803831d35Sstevel 50903831d35Sstevel uint64_t ntwdt_debug; /* enables tracing of module's activity */ 51003831d35Sstevel 51103831d35Sstevel /* used in non-debug version of module */ 51203831d35Sstevel #define NTWDT_NDBG(flag, msg) { if ((ntwdt_debug & (flag)) != 0) \ 51303831d35Sstevel (void) printf msg; } 51403831d35Sstevel 51503831d35Sstevel #ifdef DEBUG 51603831d35Sstevel typedef struct { 51703831d35Sstevel uint32_t ntwdt_wd1; 51803831d35Sstevel uint8_t ntwdt_wd2; 51903831d35Sstevel } ntwdt_data_t; 52003831d35Sstevel 52103831d35Sstevel #define NTWDTIOCSTATE _IOWR('a', 0xa, ntwdt_data_t) 52203831d35Sstevel #define NTWDTIOCPANIC _IOR('a', 0xb, uint32_t) 52303831d35Sstevel 52403831d35Sstevel /* used in debug version of module */ 52503831d35Sstevel #define NTWDT_DBG(flag, msg) { if ((ntwdt_debug & (flag)) != 0) \ 52603831d35Sstevel (void) printf msg; } 52703831d35Sstevel #else 52803831d35Sstevel #define NTWDT_DBG(flag, msg) 52903831d35Sstevel #endif 53003831d35Sstevel 53103831d35Sstevel 53203831d35Sstevel int 53303831d35Sstevel _init(void) 53403831d35Sstevel { 53503831d35Sstevel int error = 0; 53603831d35Sstevel 53703831d35Sstevel NTWDT_DBG(WDT_DBG_ENTRY, ("_init")); 53803831d35Sstevel 53903831d35Sstevel /* Initialize the soft state structures */ 54003831d35Sstevel if ((error = ddi_soft_state_init(&ntwdt_statep, 54103831d35Sstevel sizeof (ntwdt_state_t), 1)) != 0) { 54203831d35Sstevel return (error); 54303831d35Sstevel } 54403831d35Sstevel 54503831d35Sstevel /* Install the loadable module */ 54603831d35Sstevel if ((error = mod_install(&modlinkage)) != 0) { 54703831d35Sstevel ddi_soft_state_fini(&ntwdt_statep); 54803831d35Sstevel } 54903831d35Sstevel return (error); 55003831d35Sstevel } 55103831d35Sstevel 55203831d35Sstevel int 55303831d35Sstevel _info(struct modinfo *modinfop) 55403831d35Sstevel { 55503831d35Sstevel NTWDT_DBG(WDT_DBG_ENTRY, ("_info")); 55603831d35Sstevel 55703831d35Sstevel return (mod_info(&modlinkage, modinfop)); 55803831d35Sstevel } 55903831d35Sstevel 56003831d35Sstevel int 56103831d35Sstevel _fini(void) 56203831d35Sstevel { 56303831d35Sstevel int error; 56403831d35Sstevel 56503831d35Sstevel NTWDT_DBG(WDT_DBG_ENTRY, ("_fini")); 56603831d35Sstevel 56703831d35Sstevel error = mod_remove(&modlinkage); 56803831d35Sstevel if (error == 0) { 56903831d35Sstevel ddi_soft_state_fini(&ntwdt_statep); 57003831d35Sstevel } 57103831d35Sstevel 57203831d35Sstevel return (error); 57303831d35Sstevel } 57403831d35Sstevel 57503831d35Sstevel static int 57603831d35Sstevel ntwdt_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 57703831d35Sstevel { 57803831d35Sstevel int instance; 57903831d35Sstevel ntwdt_state_t *ntwdt_ptr = NULL; 58003831d35Sstevel ntwdt_wdog_t *wdog_state = NULL; 58103831d35Sstevel cyc_handler_t *hdlr = NULL; 58203831d35Sstevel 58303831d35Sstevel NTWDT_DBG(WDT_DBG_ENTRY, ("attach: dip/cmd: 0x%p/%d", 584*07d06da5SSurya Prakki (void *)dip, cmd)); 58503831d35Sstevel 58603831d35Sstevel switch (cmd) { 58703831d35Sstevel case DDI_ATTACH: 58803831d35Sstevel break; 58903831d35Sstevel 59003831d35Sstevel case DDI_RESUME: 59103831d35Sstevel return (DDI_SUCCESS); 59203831d35Sstevel 59303831d35Sstevel default: 59403831d35Sstevel return (DDI_FAILURE); 59503831d35Sstevel } 59603831d35Sstevel 59703831d35Sstevel /* see if app-wdog is supported on our config */ 59803831d35Sstevel if (ntwdt_chk_wdog_support() != 0) 59903831d35Sstevel return (DDI_FAILURE); 60003831d35Sstevel 60103831d35Sstevel /* (unsolicitedly) send SWDT state to ScApp via mailbox */ 602*07d06da5SSurya Prakki (void) ntwdt_set_swdt_state(); 60303831d35Sstevel 60403831d35Sstevel instance = ddi_get_instance(dip); 60503831d35Sstevel ASSERT(instance == 0); 60603831d35Sstevel 60703831d35Sstevel if (ddi_soft_state_zalloc(ntwdt_statep, instance) 60803831d35Sstevel != DDI_SUCCESS) { 60903831d35Sstevel return (DDI_FAILURE); 61003831d35Sstevel } 61103831d35Sstevel ntwdt_ptr = ddi_get_soft_state(ntwdt_statep, instance); 61203831d35Sstevel ASSERT(ntwdt_ptr != NULL); 61303831d35Sstevel 61403831d35Sstevel ntwdt_dip = dip; 61503831d35Sstevel 61603831d35Sstevel ntwdt_ptr->ntwdt_dip = dip; 61703831d35Sstevel ntwdt_ptr->ntwdt_cycl_id = CYCLIC_NONE; 61803831d35Sstevel mutex_init(&ntwdt_ptr->ntwdt_mutex, NULL, 61903831d35Sstevel MUTEX_DRIVER, NULL); 62003831d35Sstevel 62103831d35Sstevel /* 62203831d35Sstevel * Initialize the watchdog structure 62303831d35Sstevel */ 62403831d35Sstevel ntwdt_ptr->ntwdt_wdog_state = 62503831d35Sstevel kmem_zalloc(sizeof (ntwdt_wdog_t), KM_SLEEP); 62603831d35Sstevel wdog_state = ntwdt_ptr->ntwdt_wdog_state; 62703831d35Sstevel 62803831d35Sstevel /* 62903831d35Sstevel * Create an iblock-cookie so that ntwdt_wdog_mutex can be 63003831d35Sstevel * used at User Context and Interrupt Context. 63103831d35Sstevel */ 63203831d35Sstevel if (ddi_get_soft_iblock_cookie(dip, DDI_SOFTINT_LOW, 63303831d35Sstevel &wdog_state->ntwdt_wdog_mtx_cookie) != DDI_SUCCESS) { 63403831d35Sstevel cmn_err(CE_WARN, "init of iblock cookie failed " 63503831d35Sstevel "for ntwdt_wdog_mutex"); 63603831d35Sstevel goto err1; 63703831d35Sstevel } else { 63803831d35Sstevel mutex_init(&wdog_state->ntwdt_wdog_mutex, NULL, MUTEX_DRIVER, 63903831d35Sstevel (void *)wdog_state->ntwdt_wdog_mtx_cookie); 64003831d35Sstevel } 64103831d35Sstevel 64203831d35Sstevel mutex_init(&wdog_state->ntwdt_event_lock, NULL, 64303831d35Sstevel MUTEX_DRIVER, NULL); 64403831d35Sstevel 64503831d35Sstevel /* Cyclic fires once per second: */ 64603831d35Sstevel wdog_state->ntwdt_cyclic_interval = NANOSEC; 64703831d35Sstevel 64803831d35Sstevel /* interpret our .conf file. */ 64903831d35Sstevel (void) ntwdt_read_props(ntwdt_ptr); 65003831d35Sstevel 65103831d35Sstevel /* init the Cyclic that drives the VWDT */ 65203831d35Sstevel hdlr = &wdog_state->ntwdt_cycl_hdlr; 65303831d35Sstevel hdlr->cyh_level = CY_LOCK_LEVEL; 65403831d35Sstevel hdlr->cyh_func = ntwdt_cyclic_pat; 65503831d35Sstevel hdlr->cyh_arg = (void *)ntwdt_ptr; 65603831d35Sstevel 65703831d35Sstevel /* Register handler for SBBC Mailbox events */ 65803831d35Sstevel if (ntwdt_add_mbox_handlers(ntwdt_ptr) != DDI_SUCCESS) 65903831d35Sstevel goto err2; 66003831d35Sstevel 66103831d35Sstevel /* Softint that will be triggered by Cyclic that drives VWDT */ 66203831d35Sstevel if (ddi_add_softintr(dip, DDI_SOFTINT_LOW, &ntwdt_cyclic_softint_id, 66303831d35Sstevel NULL, NULL, ntwdt_cyclic_softint, (caddr_t)ntwdt_ptr) 66403831d35Sstevel != DDI_SUCCESS) { 66503831d35Sstevel cmn_err(CE_WARN, "failed to add cyclic softintr"); 66603831d35Sstevel goto err3; 66703831d35Sstevel } 66803831d35Sstevel 66903831d35Sstevel /* Register callbacks for various system events, e.g. panic */ 67003831d35Sstevel ntwdt_add_callbacks(ntwdt_ptr); 67103831d35Sstevel 67203831d35Sstevel /* 67303831d35Sstevel * Create Minor Node as last activity. This prevents 67403831d35Sstevel * application from accessing our implementation until it 67503831d35Sstevel * is initialized. 67603831d35Sstevel */ 67703831d35Sstevel if (ddi_create_minor_node(dip, NTWDT_MINOR_NODE, S_IFCHR, 0, 67803831d35Sstevel DDI_PSEUDO, NULL) == DDI_FAILURE) { 67903831d35Sstevel cmn_err(CE_WARN, "failed to create Minor Node: %s", 68003831d35Sstevel NTWDT_MINOR_NODE); 68103831d35Sstevel goto err4; 68203831d35Sstevel } 68303831d35Sstevel 68403831d35Sstevel /* Display our driver info in the banner */ 68503831d35Sstevel ddi_report_dev(dip); 68603831d35Sstevel 68703831d35Sstevel return (DDI_SUCCESS); 68803831d35Sstevel 68903831d35Sstevel err4: 69003831d35Sstevel ntwdt_remove_callbacks(); 69103831d35Sstevel ddi_remove_softintr(ntwdt_cyclic_softint_id); 69203831d35Sstevel err3: 693*07d06da5SSurya Prakki (void) ntwdt_remove_mbox_handlers(); 69403831d35Sstevel err2: 69503831d35Sstevel mutex_destroy(&wdog_state->ntwdt_event_lock); 69603831d35Sstevel mutex_destroy(&wdog_state->ntwdt_wdog_mutex); 69703831d35Sstevel err1: 69803831d35Sstevel kmem_free(wdog_state, sizeof (ntwdt_wdog_t)); 69903831d35Sstevel ntwdt_ptr->ntwdt_wdog_state = NULL; 70003831d35Sstevel 70103831d35Sstevel mutex_destroy(&ntwdt_ptr->ntwdt_mutex); 70203831d35Sstevel ddi_soft_state_free(ntwdt_statep, instance); 70303831d35Sstevel 70403831d35Sstevel ntwdt_dip = NULL; 70503831d35Sstevel 70603831d35Sstevel return (DDI_FAILURE); 70703831d35Sstevel } 70803831d35Sstevel 70903831d35Sstevel /* 71003831d35Sstevel * Do static checks to see if the app-wdog feature is supported in 71103831d35Sstevel * the current configuration. 71203831d35Sstevel * 71303831d35Sstevel * If the kernel debugger was booted, then we disallow the app-wdog 71403831d35Sstevel * feature, as we assume the user will be interested more in 71503831d35Sstevel * debuggability of system than its ability to support an app-wdog. 71603831d35Sstevel * (Note that the System Watchdog (SWDT) can still be available). 71703831d35Sstevel * 71803831d35Sstevel * If the currently loaded version of ScApp does not understand one 71903831d35Sstevel * of the IOSRAM mailbox messages that is specific to the app-wdog 72003831d35Sstevel * protocol, then we disallow use of the app-wdog feature (else 72103831d35Sstevel * we could have a "split-brain" scenario where Solaris supports 72203831d35Sstevel * app-wdog but ScApp doesn't). 72303831d35Sstevel * 72403831d35Sstevel * Note that there is no *dynamic* checking of whether ScApp supports 72503831d35Sstevel * the wdog protocol. Eg, if a new version of ScApp was loaded out 72603831d35Sstevel * from under Solaris, then once in AWDT mode, Solaris has no way 72703831d35Sstevel * of knowing that (a possibly older version of) ScApp was loaded. 72803831d35Sstevel */ 72903831d35Sstevel static int 73003831d35Sstevel ntwdt_chk_wdog_support() 73103831d35Sstevel { 73203831d35Sstevel int retval = ENOTSUP; 73303831d35Sstevel int rv; 73403831d35Sstevel 73503831d35Sstevel if ((boothowto & RB_DEBUG) != 0) { 73603831d35Sstevel cmn_err(CE_WARN, "kernel debugger was booted; " 73703831d35Sstevel "application watchdog is not available."); 73803831d35Sstevel return (retval); 73903831d35Sstevel } 74003831d35Sstevel 74103831d35Sstevel /* 74203831d35Sstevel * if ScApp does not support the MBOX_GET cmd, then 74303831d35Sstevel * it does not support the app-wdog feature. Also, 74403831d35Sstevel * if there is *any* type of SBBC Mailbox error at 74503831d35Sstevel * this point, we will disable the app watchdog 74603831d35Sstevel * feature. 74703831d35Sstevel */ 74803831d35Sstevel if ((rv = ntwdt_chk_sc_support()) != 0) { 74903831d35Sstevel if (rv == EINVAL) 75003831d35Sstevel cmn_err(CE_WARN, "ScApp does not support " 75103831d35Sstevel "the application watchdog feature."); 75203831d35Sstevel else 75303831d35Sstevel cmn_err(CE_WARN, "SBBC mailbox had error;" 75403831d35Sstevel "application watchdog is not available."); 75503831d35Sstevel retval = rv; 75603831d35Sstevel } else { 75703831d35Sstevel ntwdt_watchdog_available = 1; 75803831d35Sstevel retval = 0; 75903831d35Sstevel } 76003831d35Sstevel 76103831d35Sstevel NTWDT_DBG(WDT_DBG_PROT, ("app-wdog is %savailable", 76203831d35Sstevel (ntwdt_watchdog_available != 0) ? "" : "not ")); 76303831d35Sstevel 76403831d35Sstevel return (retval); 76503831d35Sstevel } 76603831d35Sstevel 76703831d35Sstevel /* 76803831d35Sstevel * Check to see if ScApp supports the app-watchdog feature. 76903831d35Sstevel * 77003831d35Sstevel * Do this by sending one of the mailbox commands that is 77103831d35Sstevel * specific to the app-wdog protocol. If ScApp does not 77203831d35Sstevel * return an error code, we will assume it understands it 77303831d35Sstevel * (as well as the remainder of the app-wdog protocol). 77403831d35Sstevel * 77503831d35Sstevel * Notes: 77603831d35Sstevel * ntwdt_lomcmd() will return EINVAL if ScApp does not 77703831d35Sstevel * understand the message. The underlying sbbc_mbox_ 77803831d35Sstevel * utility function returns SG_MBOX_STATUS_ILLEGAL_PARAMETER 77903831d35Sstevel * ("illegal ioctl parameter"). 78003831d35Sstevel */ 78103831d35Sstevel static int 78203831d35Sstevel ntwdt_chk_sc_support() 78303831d35Sstevel { 78403831d35Sstevel lw8_get_wdt_t get_wdt; 78503831d35Sstevel 78603831d35Sstevel return (ntwdt_lomcmd(LW8_MBOX_WDT_GET, (intptr_t)&get_wdt)); 78703831d35Sstevel } 78803831d35Sstevel 78903831d35Sstevel static int 79003831d35Sstevel ntwdt_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 79103831d35Sstevel { 79203831d35Sstevel int instance = ddi_get_instance(dip); 79303831d35Sstevel ntwdt_state_t *ntwdt_ptr = NULL; 79403831d35Sstevel 79503831d35Sstevel NTWDT_DBG(WDT_DBG_ENTRY, ("detach: dip/cmd: 0x%p/%d", 796*07d06da5SSurya Prakki (void *)dip, cmd)); 79703831d35Sstevel 79803831d35Sstevel ntwdt_ptr = ddi_get_soft_state(ntwdt_statep, instance); 79903831d35Sstevel if (ntwdt_ptr == NULL) { 80003831d35Sstevel return (DDI_FAILURE); 80103831d35Sstevel } 80203831d35Sstevel 80303831d35Sstevel switch (cmd) { 80403831d35Sstevel case DDI_SUSPEND: 80503831d35Sstevel return (DDI_SUCCESS); 80603831d35Sstevel 80703831d35Sstevel case DDI_DETACH: 80803831d35Sstevel /* 80903831d35Sstevel * release resources in opposite (LIFO) order as 81003831d35Sstevel * were allocated in attach(9f). 81103831d35Sstevel */ 81203831d35Sstevel ddi_remove_minor_node(dip, NULL); 81303831d35Sstevel 81403831d35Sstevel ntwdt_stop_timer_lock((void *)ntwdt_ptr); 81503831d35Sstevel 816*07d06da5SSurya Prakki ntwdt_remove_callbacks(); 81703831d35Sstevel 81803831d35Sstevel ddi_remove_softintr(ntwdt_cyclic_softint_id); 81903831d35Sstevel 820*07d06da5SSurya Prakki (void) ntwdt_remove_mbox_handlers(); 82103831d35Sstevel 82203831d35Sstevel mutex_destroy(&ntwdt_ptr->ntwdt_wdog_state->ntwdt_event_lock); 82303831d35Sstevel mutex_destroy(&ntwdt_ptr->ntwdt_wdog_state->ntwdt_wdog_mutex); 82403831d35Sstevel kmem_free(ntwdt_ptr->ntwdt_wdog_state, 82503831d35Sstevel sizeof (ntwdt_wdog_t)); 82603831d35Sstevel ntwdt_ptr->ntwdt_wdog_state = NULL; 82703831d35Sstevel 82803831d35Sstevel mutex_destroy(&ntwdt_ptr->ntwdt_mutex); 82903831d35Sstevel 83003831d35Sstevel ddi_soft_state_free(ntwdt_statep, instance); 83103831d35Sstevel 83203831d35Sstevel ntwdt_dip = NULL; 83303831d35Sstevel return (DDI_SUCCESS); 83403831d35Sstevel 83503831d35Sstevel default: 83603831d35Sstevel return (DDI_FAILURE); 83703831d35Sstevel } 83803831d35Sstevel } 83903831d35Sstevel 84003831d35Sstevel /* 84103831d35Sstevel * Register the SBBC Mailbox handlers. 84203831d35Sstevel * 84303831d35Sstevel * Currently, only one handler is used. It processes the MBOX_EVENT_LW8 84403831d35Sstevel * Events that are sent by ScApp. Of the Events that are sent, only 84503831d35Sstevel * the Event declaring that ScApp is coming up from a reboot 84603831d35Sstevel * (LW8_EVENT_SC_RESTARTED) is processed. 84703831d35Sstevel * 84803831d35Sstevel * sbbc_mbox_reg_intr registers the handler so that it executes at 84903831d35Sstevel * a DDI_SOFTINT_MED priority. 85003831d35Sstevel */ 85103831d35Sstevel static int 85203831d35Sstevel ntwdt_add_mbox_handlers(ntwdt_state_t *ntwdt_ptr) 85303831d35Sstevel { 85403831d35Sstevel int err; 85503831d35Sstevel 85603831d35Sstevel /* 85703831d35Sstevel * We need two interrupt handlers to handle the SBBC mbox 85803831d35Sstevel * events. The sbbc_mbox_xxx implementation will 85903831d35Sstevel * trigger our ntwdt_event_data_handler, which itself will 86003831d35Sstevel * trigger our ntwdt_mbox_softint. As a result, we'll 86103831d35Sstevel * register ntwdt_mbox_softint first, to ensure it cannot 86203831d35Sstevel * be called (until its caller, ntwdt_event_data_handler) 86303831d35Sstevel * is registered. 86403831d35Sstevel */ 86503831d35Sstevel 86603831d35Sstevel /* 86703831d35Sstevel * add the softint that will do the real work of handling the 86803831d35Sstevel * LW8_SC_RESTARTED_EVENT sent from ScApp. 86903831d35Sstevel */ 87003831d35Sstevel if (ddi_add_softintr(ntwdt_ptr->ntwdt_dip, DDI_SOFTINT_LOW, 87103831d35Sstevel &ntwdt_mbox_softint_id, NULL, NULL, ntwdt_mbox_softint, 87203831d35Sstevel (caddr_t)ntwdt_ptr) != DDI_SUCCESS) { 87303831d35Sstevel cmn_err(CE_WARN, "Failed to add MBOX_EVENT_LW8 softintr"); 87403831d35Sstevel return (DDI_FAILURE); 87503831d35Sstevel } 87603831d35Sstevel 87703831d35Sstevel /* 87803831d35Sstevel * Register an interrupt handler with the SBBC mailbox utility. 87903831d35Sstevel * This handler will get called on each event of each type of 88003831d35Sstevel * MBOX_EVENT_LW8 events. However, it will only conditionally 88103831d35Sstevel * trigger the worker-handler (ntwdt_mbox_softintr). 88203831d35Sstevel */ 88303831d35Sstevel sbbc_msg.msg_buf = (caddr_t)&lw8_event; 88403831d35Sstevel sbbc_msg.msg_len = sizeof (lw8_event); 88503831d35Sstevel 88603831d35Sstevel err = sbbc_mbox_reg_intr(MBOX_EVENT_LW8, ntwdt_event_data_handler, 88703831d35Sstevel &sbbc_msg, NULL, &ntwdt_ptr->ntwdt_wdog_state->ntwdt_event_lock); 88803831d35Sstevel if (err != 0) { 88903831d35Sstevel cmn_err(CE_WARN, "Failed to register SBBC MBOX_EVENT_LW8" 89003831d35Sstevel " handler. err=%d", err); 89103831d35Sstevel 89203831d35Sstevel ddi_remove_softintr(ntwdt_mbox_softint_id); 89303831d35Sstevel return (DDI_FAILURE); 89403831d35Sstevel } 89503831d35Sstevel 89603831d35Sstevel return (DDI_SUCCESS); 89703831d35Sstevel } 89803831d35Sstevel 89903831d35Sstevel /* 90003831d35Sstevel * Unregister the SBBC Mailbox handlers that were registered 90103831d35Sstevel * by ntwdt_add_mbox_handlers. 90203831d35Sstevel */ 90303831d35Sstevel static int 90403831d35Sstevel ntwdt_remove_mbox_handlers(void) 90503831d35Sstevel { 90603831d35Sstevel int rv = DDI_SUCCESS; 90703831d35Sstevel int err; 90803831d35Sstevel 90903831d35Sstevel /* 91003831d35Sstevel * unregister the two handlers that cooperate to handle 91103831d35Sstevel * the LW8_SC_RESTARTED_EVENT. Note that they are unregistered 91203831d35Sstevel * in LIFO order (as compared to how they were registered). 91303831d35Sstevel */ 91403831d35Sstevel err = sbbc_mbox_unreg_intr(MBOX_EVENT_LW8, ntwdt_event_data_handler); 91503831d35Sstevel if (err != 0) { 91603831d35Sstevel cmn_err(CE_WARN, "Failed to unregister sbbc MBOX_EVENT_LW8 " 91703831d35Sstevel "handler. Err=%d", err); 91803831d35Sstevel rv = DDI_FAILURE; 91903831d35Sstevel } 92003831d35Sstevel 92103831d35Sstevel /* remove the associated softint */ 92203831d35Sstevel ddi_remove_softintr(ntwdt_mbox_softint_id); 92303831d35Sstevel 92403831d35Sstevel return (rv); 92503831d35Sstevel } 92603831d35Sstevel 92703831d35Sstevel _NOTE(ARGSUSED(0)) 92803831d35Sstevel static int 92903831d35Sstevel ntwdt_info(dev_info_t *dip, ddi_info_cmd_t infocmd, 93003831d35Sstevel void *arg, void **result) 93103831d35Sstevel { 93203831d35Sstevel dev_t dev; 93303831d35Sstevel int instance; 93403831d35Sstevel int error = DDI_SUCCESS; 93503831d35Sstevel 93603831d35Sstevel if (result == NULL) 93703831d35Sstevel return (DDI_FAILURE); 93803831d35Sstevel 93903831d35Sstevel switch (infocmd) { 94003831d35Sstevel case DDI_INFO_DEVT2DEVINFO: 94103831d35Sstevel dev = (dev_t)arg; 94203831d35Sstevel if (getminor(dev) == 0) 94303831d35Sstevel *result = (void *)ntwdt_dip; 94403831d35Sstevel else 94503831d35Sstevel error = DDI_FAILURE; 94603831d35Sstevel break; 94703831d35Sstevel 94803831d35Sstevel case DDI_INFO_DEVT2INSTANCE: 94903831d35Sstevel dev = (dev_t)arg; 95003831d35Sstevel instance = getminor(dev); 95103831d35Sstevel *result = (void *)(uintptr_t)instance; 95203831d35Sstevel break; 95303831d35Sstevel 95403831d35Sstevel default: 95503831d35Sstevel error = DDI_FAILURE; 95603831d35Sstevel } 95703831d35Sstevel 95803831d35Sstevel return (error); 95903831d35Sstevel } 96003831d35Sstevel 96103831d35Sstevel /* 96203831d35Sstevel * Open the device this driver manages. 96303831d35Sstevel * 96403831d35Sstevel * Ensure the caller is a privileged process, else 96503831d35Sstevel * a non-privileged user could cause denial-of-service 96603831d35Sstevel * and/or negatively impact reliability/availability. 96703831d35Sstevel * 96803831d35Sstevel * Ensure there is only one concurrent open(). 96903831d35Sstevel */ 97003831d35Sstevel _NOTE(ARGSUSED(1)) 97103831d35Sstevel static int 97203831d35Sstevel ntwdt_open(dev_t *devp, int flag, int otyp, cred_t *credp) 97303831d35Sstevel { 97403831d35Sstevel int inst = getminor(*devp); 97503831d35Sstevel int ret = 0; 97603831d35Sstevel ntwdt_state_t *ntwdt_ptr = getstate(inst); 97703831d35Sstevel 97803831d35Sstevel NTWDT_DBG(WDT_DBG_ENTRY, ("open: inst/soft: %d/0x%p", 979*07d06da5SSurya Prakki inst, (void *)ntwdt_ptr)); 98003831d35Sstevel 98103831d35Sstevel /* ensure caller is a privileged process */ 98203831d35Sstevel if (drv_priv(credp) != 0) 98303831d35Sstevel return (EPERM); 98403831d35Sstevel 98503831d35Sstevel /* 98603831d35Sstevel * Check for a Deferred Attach scenario. 98703831d35Sstevel * Return ENXIO so DDI framework will call 98803831d35Sstevel * attach() and then retry the open(). 98903831d35Sstevel */ 99003831d35Sstevel if (ntwdt_ptr == NULL) 99103831d35Sstevel return (ENXIO); 99203831d35Sstevel 99303831d35Sstevel mutex_enter(&ntwdt_ptr->ntwdt_wdog_state->ntwdt_wdog_mutex); 99403831d35Sstevel mutex_enter(&ntwdt_ptr->ntwdt_mutex); 99503831d35Sstevel if (ntwdt_ptr->ntwdt_open_flag != 0) 99603831d35Sstevel ret = EAGAIN; 99703831d35Sstevel else 99803831d35Sstevel ntwdt_ptr->ntwdt_open_flag = 1; 99903831d35Sstevel mutex_exit(&ntwdt_ptr->ntwdt_mutex); 100003831d35Sstevel mutex_exit(&ntwdt_ptr->ntwdt_wdog_state->ntwdt_wdog_mutex); 100103831d35Sstevel 100203831d35Sstevel return (ret); 100303831d35Sstevel } 100403831d35Sstevel 100503831d35Sstevel /* 100603831d35Sstevel * Close the device this driver manages. 100703831d35Sstevel * 100803831d35Sstevel * Notes: 100903831d35Sstevel * 101003831d35Sstevel * The close() can happen while the AWDT is running ! 101103831d35Sstevel * (and nothing is done, eg, to disable the watchdog 101203831d35Sstevel * or to stop updating the system heartbeat). This 101303831d35Sstevel * is the desired behavior, as this allows for the 101403831d35Sstevel * case of monitoring a Solaris reboot in terms 101503831d35Sstevel * of watchdog expiration. 101603831d35Sstevel */ 101703831d35Sstevel _NOTE(ARGSUSED(1)) 101803831d35Sstevel static int 101903831d35Sstevel ntwdt_close(dev_t dev, int flag, int otyp, cred_t *credp) 102003831d35Sstevel { 102103831d35Sstevel int inst = getminor(dev); 102203831d35Sstevel ntwdt_state_t *ntwdt_ptr = getstate(inst); 102303831d35Sstevel 102403831d35Sstevel NTWDT_DBG(WDT_DBG_ENTRY, ("close: inst/soft: %d/0x%p", 1025*07d06da5SSurya Prakki inst, (void *)ntwdt_ptr)); 102603831d35Sstevel 102703831d35Sstevel if (ntwdt_ptr == NULL) 102803831d35Sstevel return (ENXIO); 102903831d35Sstevel 103003831d35Sstevel mutex_enter(&ntwdt_ptr->ntwdt_wdog_state->ntwdt_wdog_mutex); 103103831d35Sstevel mutex_enter(&ntwdt_ptr->ntwdt_mutex); 103203831d35Sstevel if (ntwdt_ptr->ntwdt_open_flag != 0) { 103303831d35Sstevel ntwdt_ptr->ntwdt_open_flag = 0; 103403831d35Sstevel } 103503831d35Sstevel mutex_exit(&ntwdt_ptr->ntwdt_mutex); 103603831d35Sstevel mutex_exit(&ntwdt_ptr->ntwdt_wdog_state->ntwdt_wdog_mutex); 103703831d35Sstevel 103803831d35Sstevel return (0); 103903831d35Sstevel } 104003831d35Sstevel 104103831d35Sstevel _NOTE(ARGSUSED(4)) 104203831d35Sstevel static int 104303831d35Sstevel ntwdt_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, 104403831d35Sstevel cred_t *credp, int *rvalp) 104503831d35Sstevel { 104603831d35Sstevel int inst = getminor(dev); 104703831d35Sstevel int retval = 0; 104803831d35Sstevel ntwdt_state_t *ntwdt_ptr = NULL; 104903831d35Sstevel ntwdt_wdog_t *wdog_state; 105003831d35Sstevel 105103831d35Sstevel if ((ntwdt_ptr = getstate(inst)) == NULL) 105203831d35Sstevel return (ENXIO); 105303831d35Sstevel 105403831d35Sstevel /* Only allow ioctl's if Solaris/ScApp support app-wdog */ 105503831d35Sstevel if (ntwdt_watchdog_available == 0) 105603831d35Sstevel return (ENXIO); 105703831d35Sstevel 105803831d35Sstevel wdog_state = ntwdt_ptr->ntwdt_wdog_state; 105903831d35Sstevel 106003831d35Sstevel switch (cmd) { 106103831d35Sstevel case LOMIOCDOGSTATE: { 106203831d35Sstevel /* 106303831d35Sstevel * Return the state of the AWDT to the application. 106403831d35Sstevel */ 106503831d35Sstevel lom_dogstate_t lom_dogstate; 106603831d35Sstevel 106703831d35Sstevel mutex_enter(&wdog_state->ntwdt_wdog_mutex); 106803831d35Sstevel lom_dogstate.reset_enable = 106903831d35Sstevel wdog_state->ntwdt_reset_enabled; 107003831d35Sstevel lom_dogstate.dog_enable = 107103831d35Sstevel wdog_state->ntwdt_wdog_enabled; 107203831d35Sstevel lom_dogstate.dog_timeout = 107303831d35Sstevel wdog_state->ntwdt_wdog_timeout; 107403831d35Sstevel mutex_exit(&wdog_state->ntwdt_wdog_mutex); 107503831d35Sstevel 107603831d35Sstevel NTWDT_DBG(WDT_DBG_IOCTL, ("DOGSTATE: wdog/reset/timeout:" 107703831d35Sstevel " %d/%d/%d", lom_dogstate.dog_enable, 107803831d35Sstevel lom_dogstate.reset_enable, lom_dogstate.dog_timeout)); 107903831d35Sstevel 108003831d35Sstevel if (ddi_copyout((caddr_t)&lom_dogstate, (caddr_t)arg, 108103831d35Sstevel sizeof (lom_dogstate_t), mode) != 0) { 108203831d35Sstevel retval = EFAULT; 108303831d35Sstevel } 108403831d35Sstevel break; 108503831d35Sstevel } 108603831d35Sstevel 108703831d35Sstevel case LOMIOCDOGCTL: { 108803831d35Sstevel /* 108903831d35Sstevel * Allow application to control whether watchdog 109003831d35Sstevel * is {dis,en}abled and whether Reset is 109103831d35Sstevel * {dis,en}abled. 109203831d35Sstevel */ 109303831d35Sstevel lom_dogctl_t lom_dogctl; 109403831d35Sstevel 109503831d35Sstevel if (ddi_copyin((caddr_t)arg, (caddr_t)&lom_dogctl, 109603831d35Sstevel sizeof (lom_dogctl_t), mode) != 0) { 109703831d35Sstevel retval = EFAULT; 109803831d35Sstevel break; 109903831d35Sstevel } 110003831d35Sstevel 110103831d35Sstevel NTWDT_DBG(WDT_DBG_IOCTL, ("DOGCTL: wdog/reset:" 110203831d35Sstevel " %d/%d", lom_dogctl.dog_enable, 110303831d35Sstevel lom_dogctl.reset_enable)); 110403831d35Sstevel 110503831d35Sstevel mutex_enter(&wdog_state->ntwdt_wdog_mutex); 110603831d35Sstevel 110703831d35Sstevel if (wdog_state->ntwdt_wdog_timeout == 0) { 110803831d35Sstevel /* 110903831d35Sstevel * then LOMIOCDOGTIME has never been used 111003831d35Sstevel * to setup a valid timeout. 111103831d35Sstevel */ 111203831d35Sstevel retval = EINVAL; 111303831d35Sstevel goto end; 111403831d35Sstevel } 111503831d35Sstevel 111603831d35Sstevel /* 111703831d35Sstevel * Return error for the non-sensical combination: 111803831d35Sstevel * "enable Reset" and "disable watchdog". 111903831d35Sstevel */ 112003831d35Sstevel if (lom_dogctl.dog_enable == 0 && 112103831d35Sstevel lom_dogctl.reset_enable != 0) { 112203831d35Sstevel retval = EINVAL; 112303831d35Sstevel goto end; 112403831d35Sstevel } 112503831d35Sstevel 112603831d35Sstevel /* 112703831d35Sstevel * Store the user-specified state in our softstate. 112803831d35Sstevel * Note that our implementation here is stateless. 112903831d35Sstevel * Eg, we do not disallow an "enable the watchdog" 113003831d35Sstevel * command when the watchdog is currently enabled. 113103831d35Sstevel * This is needed (at least in the case) when 113203831d35Sstevel * the user enters OBP via ScApp/lom. In that case, 113303831d35Sstevel * ScApp disables the watchdog, but does not inform 113403831d35Sstevel * Solaris. As a result, an ensuing, unfiltered DOGCTL 113503831d35Sstevel * to enable the watchdog is required. 113603831d35Sstevel */ 113703831d35Sstevel wdog_state->ntwdt_reset_enabled = 113803831d35Sstevel lom_dogctl.reset_enable; 113903831d35Sstevel wdog_state->ntwdt_wdog_enabled = 114003831d35Sstevel lom_dogctl.dog_enable; 114103831d35Sstevel 114203831d35Sstevel if (wdog_state->ntwdt_wdog_enabled != 0) { 114303831d35Sstevel /* 114403831d35Sstevel * then user wants to enable watchdog. 114503831d35Sstevel * Arm the watchdog timer and start the 114603831d35Sstevel * Cyclic, if it is not running. 114703831d35Sstevel */ 114803831d35Sstevel ntwdt_arm_vwdt(wdog_state); 114903831d35Sstevel 115003831d35Sstevel if (wdog_state->ntwdt_timer_running == 0) { 115103831d35Sstevel ntwdt_start_timer(ntwdt_ptr); 115203831d35Sstevel } 115303831d35Sstevel } else { 115403831d35Sstevel /* 115503831d35Sstevel * user wants to disable the watchdog. 115603831d35Sstevel * Note that we do not set ntwdt_secs_remaining 115703831d35Sstevel * to zero; that could cause a false expiration. 115803831d35Sstevel */ 115903831d35Sstevel if (wdog_state->ntwdt_timer_running != 0) { 116003831d35Sstevel ntwdt_stop_timer(ntwdt_ptr); 116103831d35Sstevel } 116203831d35Sstevel } 116303831d35Sstevel 116403831d35Sstevel /* 116503831d35Sstevel * Send a permutation of mailbox commands to 116603831d35Sstevel * ScApp that describes the current state of the 116703831d35Sstevel * watchdog timer. Note that the permutation 116803831d35Sstevel * depends on whether this is the first 116903831d35Sstevel * Enabling of the watchdog or not. 117003831d35Sstevel */ 117103831d35Sstevel if (wdog_state->ntwdt_wdog_enabled != 0 && 117203831d35Sstevel wdog_state->ntwdt_is_initial_enable == 0) { 117303831d35Sstevel 117403831d35Sstevel /* switch from SWDT to AWDT mode */ 117503831d35Sstevel ntwdt_swdt_to_awdt(wdog_state); 117603831d35Sstevel 117703831d35Sstevel /* Tell ScApp we're in AWDT mode */ 1178*07d06da5SSurya Prakki (void) ntwdt_set_cfgvar(LW8_WDT_PROP_MODE, 117903831d35Sstevel LW8_PROP_MODE_AWDT); 118003831d35Sstevel } 118103831d35Sstevel 118203831d35Sstevel /* Inform ScApp of the choices made by the app */ 1183*07d06da5SSurya Prakki (void) ntwdt_set_cfgvar(LW8_WDT_PROP_WDT, 118403831d35Sstevel wdog_state->ntwdt_wdog_enabled); 1185*07d06da5SSurya Prakki (void) ntwdt_set_cfgvar(LW8_WDT_PROP_RECOV, 118603831d35Sstevel wdog_state->ntwdt_reset_enabled); 118703831d35Sstevel 118803831d35Sstevel if (wdog_state->ntwdt_wdog_enabled != 0 && 118903831d35Sstevel wdog_state->ntwdt_is_initial_enable == 0) { 119003831d35Sstevel /* 119103831d35Sstevel * Clear tod_iosram_t.tod_timeout_period, 119203831d35Sstevel * which is used in SWDT part of state 119303831d35Sstevel * machine. (If this field is non-zero, 119403831d35Sstevel * ScApp assumes that Solaris' SWDT is active). 119503831d35Sstevel * 119603831d35Sstevel * Clearing this is useful in case SC reboots 119703831d35Sstevel * while Solaris is running, as ScApp will read 119803831d35Sstevel * a zero and not assume SWDT is running. 119903831d35Sstevel */ 1200*07d06da5SSurya Prakki (void) ntwdt_set_hw_timeout(0); 120103831d35Sstevel 120203831d35Sstevel /* "the first watchdog-enable has been seen" */ 120303831d35Sstevel wdog_state->ntwdt_is_initial_enable = 1; 120403831d35Sstevel } 120503831d35Sstevel 120603831d35Sstevel mutex_exit(&wdog_state->ntwdt_wdog_mutex); 120703831d35Sstevel break; 120803831d35Sstevel } 120903831d35Sstevel 121003831d35Sstevel case LOMIOCDOGTIME: { 121103831d35Sstevel /* 121203831d35Sstevel * Allow application to set the period (in seconds) 121303831d35Sstevel * of the watchdog timeout. 121403831d35Sstevel */ 121503831d35Sstevel uint32_t lom_dogtime; 121603831d35Sstevel 121703831d35Sstevel if (ddi_copyin((caddr_t)arg, (caddr_t)&lom_dogtime, 121803831d35Sstevel sizeof (uint32_t), mode) != 0) { 121903831d35Sstevel retval = EFAULT; 122003831d35Sstevel break; 122103831d35Sstevel } 122203831d35Sstevel 122303831d35Sstevel NTWDT_DBG(WDT_DBG_IOCTL, ("DOGTIME: %u seconds", 122403831d35Sstevel lom_dogtime)); 122503831d35Sstevel 122603831d35Sstevel /* Ensure specified timeout is within range. */ 122703831d35Sstevel if ((lom_dogtime == 0) || 122803831d35Sstevel (lom_dogtime > NTWDT_MAX_TIMEOUT)) { 122903831d35Sstevel retval = EINVAL; 123003831d35Sstevel break; 123103831d35Sstevel } 123203831d35Sstevel 123303831d35Sstevel mutex_enter(&wdog_state->ntwdt_wdog_mutex); 123403831d35Sstevel 123503831d35Sstevel wdog_state->ntwdt_wdog_timeout = lom_dogtime; 123603831d35Sstevel 123703831d35Sstevel /* 123803831d35Sstevel * If watchdog is currently running, re-arm the 123903831d35Sstevel * watchdog timeout with the specified value. 124003831d35Sstevel */ 124103831d35Sstevel if (wdog_state->ntwdt_timer_running != 0) { 124203831d35Sstevel ntwdt_arm_vwdt(wdog_state); 124303831d35Sstevel } 124403831d35Sstevel 124503831d35Sstevel /* Tell ScApp of the specified timeout */ 1246*07d06da5SSurya Prakki (void) ntwdt_set_cfgvar(LW8_WDT_PROP_TO, lom_dogtime); 124703831d35Sstevel 124803831d35Sstevel mutex_exit(&wdog_state->ntwdt_wdog_mutex); 124903831d35Sstevel break; 125003831d35Sstevel } 125103831d35Sstevel 125203831d35Sstevel case LOMIOCDOGPAT: { 125303831d35Sstevel /* 125403831d35Sstevel * Allow user to re-arm ("pat") the watchdog. 125503831d35Sstevel */ 125603831d35Sstevel NTWDT_DBG(WDT_DBG_IOCTL, ("DOGPAT")); 125703831d35Sstevel 125803831d35Sstevel mutex_enter(&wdog_state->ntwdt_wdog_mutex); 125903831d35Sstevel 126003831d35Sstevel /* 126103831d35Sstevel * If watchdog is not enabled or underlying 126203831d35Sstevel * Cyclic timer is not running, exit. 126303831d35Sstevel */ 126403831d35Sstevel if (!(wdog_state->ntwdt_wdog_enabled && 126503831d35Sstevel wdog_state->ntwdt_timer_running)) 126603831d35Sstevel goto end; 126703831d35Sstevel 126803831d35Sstevel if (wdog_state->ntwdt_wdog_expired == 0) { 126903831d35Sstevel /* then VWDT has not expired; re-arm it */ 127003831d35Sstevel ntwdt_arm_vwdt(wdog_state); 127103831d35Sstevel 127203831d35Sstevel NTWDT_DBG(WDT_DBG_VWDT, ("VWDT re-armed:" 127303831d35Sstevel " %d seconds", 127403831d35Sstevel wdog_state->ntwdt_secs_remaining)); 127503831d35Sstevel } 127603831d35Sstevel 127703831d35Sstevel mutex_exit(&wdog_state->ntwdt_wdog_mutex); 127803831d35Sstevel break; 127903831d35Sstevel } 128003831d35Sstevel 128103831d35Sstevel #ifdef DEBUG 128203831d35Sstevel case NTWDTIOCPANIC: { 128303831d35Sstevel /* 128403831d35Sstevel * Use in unit/integration testing to test our 128503831d35Sstevel * panic-handler code. 128603831d35Sstevel */ 128703831d35Sstevel cmn_err(CE_PANIC, "NTWDTIOCPANIC: force a panic"); 128803831d35Sstevel break; 128903831d35Sstevel } 129003831d35Sstevel 129103831d35Sstevel case NTWDTIOCSTATE: { 129203831d35Sstevel /* 129303831d35Sstevel * Allow application to read wdog state from the 129403831d35Sstevel * SC (and *not* the driver's softstate). 129503831d35Sstevel * 129603831d35Sstevel * Return state of: 129703831d35Sstevel * o recovery-enabled 129803831d35Sstevel * o current timeout value 129903831d35Sstevel */ 130003831d35Sstevel ntwdt_data_t ntwdt_data; 130103831d35Sstevel int action; 130203831d35Sstevel int timeout; 130303831d35Sstevel int ret; 130403831d35Sstevel 130503831d35Sstevel mutex_enter(&wdog_state->ntwdt_wdog_mutex); 130603831d35Sstevel ret = ntwdt_get_cfgvar(LW8_WDT_PROP_TO, &timeout); 130703831d35Sstevel ret |= ntwdt_get_cfgvar(LW8_WDT_PROP_RECOV, &action); 130803831d35Sstevel mutex_exit(&wdog_state->ntwdt_wdog_mutex); 130903831d35Sstevel 131003831d35Sstevel bzero((caddr_t)&ntwdt_data, sizeof (ntwdt_data)); 131103831d35Sstevel 131203831d35Sstevel if (ret != NTWDT_SUCCESS) { 131303831d35Sstevel retval = EIO; 131403831d35Sstevel break; 131503831d35Sstevel } 131603831d35Sstevel 131703831d35Sstevel NTWDT_DBG(WDT_DBG_IOCTL, ("NTWDTIOCSTATE:" 131803831d35Sstevel " timeout/action: %d/%d", timeout, action)); 131903831d35Sstevel 132003831d35Sstevel ntwdt_data.ntwdt_wd1 = (uint32_t)timeout; 132103831d35Sstevel ntwdt_data.ntwdt_wd2 = (uint8_t)action; 132203831d35Sstevel 132303831d35Sstevel if (ddi_copyout((caddr_t)&ntwdt_data, (caddr_t)arg, 132403831d35Sstevel sizeof (ntwdt_data_t), mode) != 0) { 132503831d35Sstevel retval = EFAULT; 132603831d35Sstevel } 132703831d35Sstevel break; 132803831d35Sstevel } 132903831d35Sstevel #endif 133003831d35Sstevel default: 133103831d35Sstevel retval = EINVAL; 133203831d35Sstevel break; 133303831d35Sstevel } 133403831d35Sstevel 133503831d35Sstevel return (retval); 133603831d35Sstevel end: 133703831d35Sstevel mutex_exit(&wdog_state->ntwdt_wdog_mutex); 133803831d35Sstevel return (retval); 133903831d35Sstevel } 134003831d35Sstevel 134103831d35Sstevel /* 134203831d35Sstevel * Arm the Virtual Watchdog Timer (VWDT). 134303831d35Sstevel * 134403831d35Sstevel * Assign the current watchdog timeout (ntwdt_wdog_timeout) 134503831d35Sstevel * to the softstate variable representing the watchdog 134603831d35Sstevel * timer (ntwdt_secs_remaining). 134703831d35Sstevel * 134803831d35Sstevel * To ensure (from ntwdt's perspective) that any actual 134903831d35Sstevel * timeout expiration is at least as large as the expected 135003831d35Sstevel * timeout, conditionally set/clear a bit that will be 135103831d35Sstevel * checked in the Cyclic's softint. 135203831d35Sstevel * 135303831d35Sstevel * If the Cyclic has been started, the goal is to ignore 135403831d35Sstevel * the _next_ firing of the Cyclic, as that firing will 135503831d35Sstevel * NOT represent a full, one-second period. If the Cyclic 135603831d35Sstevel * has NOT been started yet, then do not ignore the next 135703831d35Sstevel * Cyclic's firing, as that's the First One, and it was 135803831d35Sstevel * programmed to fire at a specific time (see ntwdt_start_timer). 135903831d35Sstevel */ 136003831d35Sstevel static void 136103831d35Sstevel ntwdt_arm_vwdt(ntwdt_wdog_t *wdog_state) 136203831d35Sstevel { 136303831d35Sstevel /* arm the watchdog timer (VWDT) */ 136403831d35Sstevel wdog_state->ntwdt_secs_remaining = 136503831d35Sstevel wdog_state->ntwdt_wdog_timeout; 136603831d35Sstevel 136703831d35Sstevel if (wdog_state->ntwdt_timer_running != 0) 136803831d35Sstevel NTWDT_FLAG_SET(wdog_state, SKIP_CYCLIC); 136903831d35Sstevel else 137003831d35Sstevel NTWDT_FLAG_CLR(wdog_state, SKIP_CYCLIC); 137103831d35Sstevel } 137203831d35Sstevel 137303831d35Sstevel /* 137403831d35Sstevel * Switch from SWDT mode to AWDT mode. 137503831d35Sstevel */ 137603831d35Sstevel _NOTE(ARGSUSED(0)) 137703831d35Sstevel static void 137803831d35Sstevel ntwdt_swdt_to_awdt(ntwdt_wdog_t *wdog_state) 137903831d35Sstevel { 138003831d35Sstevel ASSERT(wdog_state->ntwdt_is_initial_enable == 0); 138103831d35Sstevel 138203831d35Sstevel /* 138303831d35Sstevel * Disable SWDT. If SWDT is currently active, 138403831d35Sstevel * display a message so user knows that SWDT Mode 138503831d35Sstevel * has terminated. 138603831d35Sstevel */ 138703831d35Sstevel if (watchdog_enable != 0 || 138803831d35Sstevel watchdog_activated != 0) 138903831d35Sstevel cmn_err(CE_NOTE, "Hardware watchdog disabled"); 139003831d35Sstevel watchdog_enable = 0; 139103831d35Sstevel watchdog_activated = 0; 139203831d35Sstevel 139303831d35Sstevel /* "we are in AWDT mode" */ 139403831d35Sstevel ntwdt_watchdog_activated = 1; 139503831d35Sstevel NTWDT_DBG(WDT_DBG_VWDT, ("AWDT is enabled")); 139603831d35Sstevel } 139703831d35Sstevel 139803831d35Sstevel /* 139903831d35Sstevel * This is the Cyclic that runs at a multiple of the 140003831d35Sstevel * AWDT's watchdog-timeout period. This Cyclic runs at 140103831d35Sstevel * LOCK_LEVEL (eg, CY_LOCK_LEVEL) and will post a 140203831d35Sstevel * soft-interrupt in order to complete all processing. 140303831d35Sstevel * 140403831d35Sstevel * Executing at LOCK_LEVEL gives this function a high 140503831d35Sstevel * interrupt priority, while performing its work via 140603831d35Sstevel * a soft-interrupt allows for a consistent (eg, MT-safe) 140703831d35Sstevel * view of driver softstate between User and Interrupt 140803831d35Sstevel * context. 140903831d35Sstevel * 141003831d35Sstevel * Context: 141103831d35Sstevel * interrupt context: Cyclic framework calls at 141203831d35Sstevel * CY_LOCK_LEVEL (=> 10) 141303831d35Sstevel */ 141403831d35Sstevel _NOTE(ARGSUSED(0)) 141503831d35Sstevel static void 141603831d35Sstevel ntwdt_cyclic_pat(void *arg) 141703831d35Sstevel { 141803831d35Sstevel /* post-down to DDI_SOFTINT_LOW */ 141903831d35Sstevel ddi_trigger_softintr(ntwdt_cyclic_softint_id); 142003831d35Sstevel } 142103831d35Sstevel 142203831d35Sstevel /* 142303831d35Sstevel * This is the soft-interrupt triggered by the AWDT 142403831d35Sstevel * Cyclic. 142503831d35Sstevel * 142603831d35Sstevel * This softint does all the work re: computing whether 142703831d35Sstevel * the VWDT expired. It grabs ntwdt_wdog_mutex 142803831d35Sstevel * so User Context code (eg, the IOCTLs) cannot run, 142903831d35Sstevel * and then it tests whether the VWDT expired. If it 143003831d35Sstevel * hasn't, it decrements the VWDT timer by the amount 143103831d35Sstevel * of the Cyclic's period. If the timer has expired, 143203831d35Sstevel * it initiates Recovery (based on what user specified 143303831d35Sstevel * in LOMIOCDOGCTL). 143403831d35Sstevel * 143503831d35Sstevel * This function also updates the normal system "heartbeat". 143603831d35Sstevel * 143703831d35Sstevel * Context: 143803831d35Sstevel * interrupt-context: DDI_SOFTINT_LOW 143903831d35Sstevel */ 144003831d35Sstevel static uint_t 144103831d35Sstevel ntwdt_cyclic_softint(char *arg) 144203831d35Sstevel { 144303831d35Sstevel ntwdt_state_t *ntwdt_ptr = (ntwdt_state_t *)arg; 144403831d35Sstevel ntwdt_wdog_t *wdog_state; 144503831d35Sstevel 144603831d35Sstevel wdog_state = ntwdt_ptr->ntwdt_wdog_state; 144703831d35Sstevel 144803831d35Sstevel mutex_enter(&wdog_state->ntwdt_wdog_mutex); 144903831d35Sstevel 145003831d35Sstevel if ((wdog_state->ntwdt_wdog_flags & 145103831d35Sstevel NTWDT_FLAG_SKIP_CYCLIC) != 0) { 145203831d35Sstevel /* 145303831d35Sstevel * then skip all processing by this interrupt. 145403831d35Sstevel * (see ntwdt_arm_vwdt()). 145503831d35Sstevel */ 145603831d35Sstevel wdog_state->ntwdt_wdog_flags &= ~NTWDT_FLAG_SKIP_CYCLIC; 145703831d35Sstevel goto end; 145803831d35Sstevel } 145903831d35Sstevel 146003831d35Sstevel if (wdog_state->ntwdt_timer_running == 0 || 146103831d35Sstevel (ntwdt_ptr->ntwdt_cycl_id == CYCLIC_NONE) || 146203831d35Sstevel (wdog_state->ntwdt_wdog_enabled == 0)) 146303831d35Sstevel goto end; 146403831d35Sstevel 146503831d35Sstevel /* re-arm ("pat") the hardware watchdog */ 146603831d35Sstevel ntwdt_pat_hw_watchdog(); 146703831d35Sstevel 146803831d35Sstevel /* Decrement the VWDT and see if it has expired. */ 146903831d35Sstevel if (--wdog_state->ntwdt_secs_remaining == 0) { 147003831d35Sstevel 147103831d35Sstevel cmn_err(CE_WARN, "application-watchdog expired"); 147203831d35Sstevel 147303831d35Sstevel wdog_state->ntwdt_wdog_expired = 1; 147403831d35Sstevel 147503831d35Sstevel if (wdog_state->ntwdt_reset_enabled != 0) { 147603831d35Sstevel /* 147703831d35Sstevel * Update ScApp so that the new wdog-timeout 147803831d35Sstevel * value is as specified in the 147903831d35Sstevel * NTWDT_BOOT_TIMEOUT_PROP driver Property. 148003831d35Sstevel * This timeout is assumedly larger than the 148103831d35Sstevel * actual Solaris reboot time. This will allow 148203831d35Sstevel * our forced-reboot to not cause an unplanned 148303831d35Sstevel * (series of) watchdog expiration(s). 148403831d35Sstevel */ 148503831d35Sstevel if (ntwdt_disable_timeout_action == 0) 148603831d35Sstevel ntwdt_reprogram_wd(ntwdt_ptr); 148703831d35Sstevel 148803831d35Sstevel mutex_exit(&wdog_state->ntwdt_wdog_mutex); 148903831d35Sstevel 149003831d35Sstevel NTWDT_DBG(WDT_DBG_VWDT, ("recovery being done")); 149103831d35Sstevel 149203831d35Sstevel ntwdt_enforce_timeout(); 149303831d35Sstevel } else { 149403831d35Sstevel NTWDT_DBG(WDT_DBG_VWDT, ("no recovery being done")); 149503831d35Sstevel 149603831d35Sstevel wdog_state->ntwdt_wdog_enabled = 0; 149703831d35Sstevel 149803831d35Sstevel /* 149903831d35Sstevel * Tell ScApp to disable wdog; this prevents 150003831d35Sstevel * the "2x-timeout" artifact. Eg, Solaris 150103831d35Sstevel * times-out at t(x) and ScApp times-out at t(2x), 150203831d35Sstevel * where (x==ntwdt_wdog_timeout). 150303831d35Sstevel */ 150403831d35Sstevel (void) ntwdt_set_cfgvar(LW8_WDT_PROP_WDT, 150503831d35Sstevel wdog_state->ntwdt_wdog_enabled); 150603831d35Sstevel } 150703831d35Sstevel 150803831d35Sstevel /* Schedule Callout to stop this Cyclic */ 1509*07d06da5SSurya Prakki (void) timeout(ntwdt_stop_timer_lock, ntwdt_ptr, 0); 151003831d35Sstevel 151103831d35Sstevel } else { 151203831d35Sstevel _NOTE(EMPTY) 151303831d35Sstevel NTWDT_DBG(WDT_DBG_VWDT, ("time remaining in VWDT: %d" 151403831d35Sstevel " seconds", wdog_state->ntwdt_secs_remaining)); 151503831d35Sstevel } 151603831d35Sstevel end: 151703831d35Sstevel mutex_exit(&wdog_state->ntwdt_wdog_mutex); 151803831d35Sstevel 151903831d35Sstevel return (DDI_INTR_CLAIMED); 152003831d35Sstevel } 152103831d35Sstevel 152203831d35Sstevel /* 152303831d35Sstevel * Program the AWDT watchdog-timeout value to that specified 152403831d35Sstevel * in the NTWDT_BOOT_TIMEOUT_PROP driver Property. However, 152503831d35Sstevel * only do this if the AWDT is in the correct state. 152603831d35Sstevel * 152703831d35Sstevel * Caller's Context: 152803831d35Sstevel * o interrupt context: (from software-interrupt) 152903831d35Sstevel * o during a panic 153003831d35Sstevel */ 153103831d35Sstevel static void 153203831d35Sstevel ntwdt_reprogram_wd(ntwdt_state_t *ntwdt_ptr) 153303831d35Sstevel { 153403831d35Sstevel ntwdt_wdog_t *wdog_state = ntwdt_ptr->ntwdt_wdog_state; 153503831d35Sstevel 153603831d35Sstevel /* 153703831d35Sstevel * Program the AWDT watchdog-timeout value only if the 153803831d35Sstevel * watchdog is enabled, the user wants to do recovery, 153903831d35Sstevel * ("reset is enabled") and the AWDT timer is currently 154003831d35Sstevel * running. 154103831d35Sstevel */ 154203831d35Sstevel if (wdog_state->ntwdt_wdog_enabled != 0 && 154303831d35Sstevel wdog_state->ntwdt_reset_enabled != 0 && 154403831d35Sstevel wdog_state->ntwdt_timer_running != 0) { 154503831d35Sstevel if (ddi_in_panic() != 0) 1546*07d06da5SSurya Prakki (void) ntwdt_set_cfgvar_noreply(LW8_WDT_PROP_TO, 154703831d35Sstevel wdog_state->ntwdt_boot_timeout); 154803831d35Sstevel else 154903831d35Sstevel (void) ntwdt_set_cfgvar(LW8_WDT_PROP_TO, 155003831d35Sstevel wdog_state->ntwdt_boot_timeout); 155103831d35Sstevel } 155203831d35Sstevel } 155303831d35Sstevel 155403831d35Sstevel /* 155503831d35Sstevel * This is the callback that was registered to run during a panic. 155603831d35Sstevel * It will set the watchdog-timeout value to be that as specified 155703831d35Sstevel * in the NTWDT_BOOT_TIMEOUT_PROP driver Property. 155803831d35Sstevel * 155903831d35Sstevel * Note that unless this Property's value specifies a timeout 156003831d35Sstevel * that's larger than the actual reboot latency, ScApp will 156103831d35Sstevel * experience a timeout and initiate Recovery. 156203831d35Sstevel */ 156303831d35Sstevel _NOTE(ARGSUSED(1)) 156403831d35Sstevel static boolean_t 156503831d35Sstevel ntwdt_panic_cb(void *arg, int code) 156603831d35Sstevel { 156703831d35Sstevel ASSERT(ddi_in_panic() != 0); 156803831d35Sstevel 156903831d35Sstevel ntwdt_reprogram_wd((ntwdt_state_t *)arg); 157003831d35Sstevel 157103831d35Sstevel return (B_TRUE); 157203831d35Sstevel } 157303831d35Sstevel 157403831d35Sstevel /* 157503831d35Sstevel * Initialize the Cyclic that is used to monitor the VWDT. 157603831d35Sstevel */ 157703831d35Sstevel static void 157803831d35Sstevel ntwdt_start_timer(ntwdt_state_t *ntwdt_ptr) 157903831d35Sstevel { 158003831d35Sstevel ntwdt_wdog_t *wdog_state = ntwdt_ptr->ntwdt_wdog_state; 158103831d35Sstevel cyc_handler_t *hdlr = &wdog_state->ntwdt_cycl_hdlr; 158203831d35Sstevel cyc_time_t *when = &wdog_state->ntwdt_cycl_time; 158303831d35Sstevel 158403831d35Sstevel /* 158503831d35Sstevel * Init Cyclic so its first expiry occurs wdog-timeout 158603831d35Sstevel * seconds from the current, absolute time. 158703831d35Sstevel */ 158803831d35Sstevel when->cyt_interval = wdog_state->ntwdt_cyclic_interval; 158903831d35Sstevel when->cyt_when = gethrtime() + when->cyt_interval; 159003831d35Sstevel 159103831d35Sstevel wdog_state->ntwdt_wdog_expired = 0; 159203831d35Sstevel wdog_state->ntwdt_timer_running = 1; 159303831d35Sstevel 159403831d35Sstevel mutex_enter(&cpu_lock); 159503831d35Sstevel if (ntwdt_ptr->ntwdt_cycl_id == CYCLIC_NONE) 159603831d35Sstevel ntwdt_ptr->ntwdt_cycl_id = cyclic_add(hdlr, when); 159703831d35Sstevel mutex_exit(&cpu_lock); 159803831d35Sstevel 159903831d35Sstevel NTWDT_DBG(WDT_DBG_VWDT, ("AWDT's cyclic-driven timer is started")); 160003831d35Sstevel } 160103831d35Sstevel 160203831d35Sstevel /* 160303831d35Sstevel * Stop the cyclic that is used to monitor the VWDT (and 160403831d35Sstevel * was Started by ntwdt_start_timer). 160503831d35Sstevel * 160603831d35Sstevel * Context: per the Cyclic API, cyclic_remove cannot be called 160703831d35Sstevel * from interrupt-context. Note that when this is 160803831d35Sstevel * called via a Callout, it's called from base level. 160903831d35Sstevel */ 161003831d35Sstevel static void 161103831d35Sstevel ntwdt_stop_timer(void *arg) 161203831d35Sstevel { 161303831d35Sstevel ntwdt_state_t *ntwdt_ptr = (void *)arg; 161403831d35Sstevel ntwdt_wdog_t *wdog_state = ntwdt_ptr->ntwdt_wdog_state; 161503831d35Sstevel 161603831d35Sstevel mutex_enter(&cpu_lock); 161703831d35Sstevel if (ntwdt_ptr->ntwdt_cycl_id != CYCLIC_NONE) 161803831d35Sstevel cyclic_remove(ntwdt_ptr->ntwdt_cycl_id); 161903831d35Sstevel mutex_exit(&cpu_lock); 162003831d35Sstevel 162103831d35Sstevel wdog_state->ntwdt_timer_running = 0; 162203831d35Sstevel ntwdt_ptr->ntwdt_cycl_id = CYCLIC_NONE; 162303831d35Sstevel 162403831d35Sstevel NTWDT_DBG(WDT_DBG_VWDT, ("AWDT's cyclic-driven timer is stopped")); 162503831d35Sstevel } 162603831d35Sstevel 162703831d35Sstevel /* 162803831d35Sstevel * Stop the cyclic that is used to monitor the VWDT (and 162903831d35Sstevel * do it in a thread-safe manner). 163003831d35Sstevel * 163103831d35Sstevel * This is a wrapper function for the core function, 163203831d35Sstevel * ntwdt_stop_timer. Both functions are useful, as some 163303831d35Sstevel * callers will already have the appropriate mutex locked, and 163403831d35Sstevel * other callers will not. 163503831d35Sstevel */ 163603831d35Sstevel static void 163703831d35Sstevel ntwdt_stop_timer_lock(void *arg) 163803831d35Sstevel { 163903831d35Sstevel ntwdt_state_t *ntwdt_ptr = (void *)arg; 164003831d35Sstevel ntwdt_wdog_t *wdog_state = ntwdt_ptr->ntwdt_wdog_state; 164103831d35Sstevel 164203831d35Sstevel mutex_enter(&wdog_state->ntwdt_wdog_mutex); 164303831d35Sstevel ntwdt_stop_timer(arg); 164403831d35Sstevel mutex_exit(&wdog_state->ntwdt_wdog_mutex); 164503831d35Sstevel } 164603831d35Sstevel 164703831d35Sstevel /* 164803831d35Sstevel * Add callbacks needed to react to major system state transitions. 164903831d35Sstevel */ 165003831d35Sstevel static void 165103831d35Sstevel ntwdt_add_callbacks(ntwdt_state_t *ntwdt_ptr) 165203831d35Sstevel { 165303831d35Sstevel /* register a callback that's called during a panic */ 165403831d35Sstevel ntwdt_callback_ids.ntwdt_panic_cb = callb_add(ntwdt_panic_cb, 165503831d35Sstevel (void *)ntwdt_ptr, CB_CL_PANIC, "ntwdt_panic_cb"); 165603831d35Sstevel } 165703831d35Sstevel 165803831d35Sstevel /* 165903831d35Sstevel * Remove callbacks added by ntwdt_add_callbacks. 166003831d35Sstevel */ 166103831d35Sstevel static void 166203831d35Sstevel ntwdt_remove_callbacks() 166303831d35Sstevel { 1664*07d06da5SSurya Prakki (void) callb_delete(ntwdt_callback_ids.ntwdt_panic_cb); 166503831d35Sstevel } 166603831d35Sstevel 166703831d35Sstevel /* 166803831d35Sstevel * Initiate a Reset (as a result of the VWDT timeout expiring). 166903831d35Sstevel */ 167003831d35Sstevel static void 167103831d35Sstevel ntwdt_enforce_timeout() 167203831d35Sstevel { 167303831d35Sstevel if (ntwdt_disable_timeout_action != 0) { 167403831d35Sstevel cmn_err(CE_NOTE, "OS timeout expired, taking no action"); 167503831d35Sstevel return; 167603831d35Sstevel } 167703831d35Sstevel 167803831d35Sstevel NTWDT_DBG(WDT_DBG_VWDT, ("VWDT expired; do a crashdump")); 167903831d35Sstevel 168003831d35Sstevel (void) kadmin(A_DUMP, AD_BOOT, NULL, kcred); 168103831d35Sstevel cmn_err(CE_PANIC, "kadmin(A_DUMP, AD_BOOT) failed"); 168203831d35Sstevel _NOTE(NOTREACHED) 168303831d35Sstevel } 168403831d35Sstevel 168503831d35Sstevel /* 168603831d35Sstevel * Interpret the Properties from driver's config file. 168703831d35Sstevel */ 168803831d35Sstevel static int 168903831d35Sstevel ntwdt_read_props(ntwdt_state_t *ntwdt_ptr) 169003831d35Sstevel { 169103831d35Sstevel ntwdt_wdog_t *wdog_state; 169203831d35Sstevel int boot_timeout; 169303831d35Sstevel 169403831d35Sstevel wdog_state = ntwdt_ptr->ntwdt_wdog_state; 169503831d35Sstevel 169603831d35Sstevel /* 169703831d35Sstevel * interpret Property that specifies how long 169803831d35Sstevel * the watchdog-timeout should be set to when 169903831d35Sstevel * Solaris panics. Assumption is that this value 170003831d35Sstevel * is larger than the amount of time it takes 170103831d35Sstevel * to reboot and write crashdump. If not, 170203831d35Sstevel * ScApp could induce a reset, due to an expired 170303831d35Sstevel * watchdog-timeout. 170403831d35Sstevel */ 170503831d35Sstevel wdog_state->ntwdt_boot_timeout = 170603831d35Sstevel NTWDT_DEFAULT_BOOT_TIMEOUT; 170703831d35Sstevel 170803831d35Sstevel boot_timeout = ddi_prop_get_int(DDI_DEV_T_ANY, 170903831d35Sstevel ntwdt_ptr->ntwdt_dip, DDI_PROP_DONTPASS, 171003831d35Sstevel NTWDT_BOOT_TIMEOUT_PROP, -1); 171103831d35Sstevel 171203831d35Sstevel if (boot_timeout != -1 && boot_timeout > 0 && 171303831d35Sstevel boot_timeout <= NTWDT_MAX_TIMEOUT) { 171403831d35Sstevel wdog_state->ntwdt_boot_timeout = 171503831d35Sstevel boot_timeout; 171603831d35Sstevel } else { 171703831d35Sstevel _NOTE(EMPTY) 171803831d35Sstevel NTWDT_DBG(WDT_DBG_ENTRY, (NTWDT_BOOT_TIMEOUT_PROP 171903831d35Sstevel ": using default of %d seconds.", 172003831d35Sstevel wdog_state->ntwdt_boot_timeout)); 172103831d35Sstevel } 172203831d35Sstevel 172303831d35Sstevel return (DDI_SUCCESS); 172403831d35Sstevel } 172503831d35Sstevel 172603831d35Sstevel /* 172703831d35Sstevel * Write state of SWDT to ScApp. 172803831d35Sstevel * 172903831d35Sstevel * Currently, this function is only called on attach() 173003831d35Sstevel * of our driver. 173103831d35Sstevel * 173203831d35Sstevel * Note that we do not need to call this function, eg, 173303831d35Sstevel * in response to a solicitation from ScApp (eg, 173403831d35Sstevel * the LW8_SC_RESTARTED_EVENT). 173503831d35Sstevel * 173603831d35Sstevel * Context: 173703831d35Sstevel * called in Kernel Context 173803831d35Sstevel */ 173903831d35Sstevel static int 174003831d35Sstevel ntwdt_set_swdt_state() 174103831d35Sstevel { 174203831d35Sstevel /* 174303831d35Sstevel * note that ScApp only needs this one 174403831d35Sstevel * variable when system is in SWDT mode. 174503831d35Sstevel */ 1746*07d06da5SSurya Prakki (void) ntwdt_set_cfgvar(LW8_WDT_PROP_MODE, 174703831d35Sstevel LW8_PROP_MODE_SWDT); 174803831d35Sstevel 174903831d35Sstevel return (0); 175003831d35Sstevel } 175103831d35Sstevel 175203831d35Sstevel /* 175303831d35Sstevel * Write all AWDT state to ScApp via the SBBC mailbox 175403831d35Sstevel * in IOSRAM. Note that the permutation of Writes 175503831d35Sstevel * is as specified in the design spec. 175603831d35Sstevel * 175703831d35Sstevel * Notes: caller must perform synchronization so that 175803831d35Sstevel * this series of Writes is consistent as viewed 175903831d35Sstevel * by ScApp (eg, there is no LW8_WDT_xxx mailbox 176003831d35Sstevel * command that contains "all Properties"; each 176103831d35Sstevel * Property must be written individually). 176203831d35Sstevel */ 176303831d35Sstevel static int 176403831d35Sstevel ntwdt_set_awdt_state(ntwdt_wdog_t *rstatep) 176503831d35Sstevel { 176603831d35Sstevel /* ScApp expects values in this order: */ 1767*07d06da5SSurya Prakki (void) ntwdt_set_cfgvar(LW8_WDT_PROP_MODE, 176803831d35Sstevel ntwdt_watchdog_activated != 0); 1769*07d06da5SSurya Prakki (void) ntwdt_set_cfgvar(LW8_WDT_PROP_TO, 177003831d35Sstevel rstatep->ntwdt_wdog_timeout); 1771*07d06da5SSurya Prakki (void) ntwdt_set_cfgvar(LW8_WDT_PROP_RECOV, 177203831d35Sstevel rstatep->ntwdt_reset_enabled); 1773*07d06da5SSurya Prakki (void) ntwdt_set_cfgvar(LW8_WDT_PROP_WDT, 177403831d35Sstevel rstatep->ntwdt_wdog_enabled); 177503831d35Sstevel 177603831d35Sstevel return (NTWDT_SUCCESS); 177703831d35Sstevel } 177803831d35Sstevel 177903831d35Sstevel /* 178003831d35Sstevel * Write a specified WDT Property (and Value) to ScApp. 178103831d35Sstevel * 178203831d35Sstevel * <Property, Value> is passed in the LW8_MBOX_WDT_SET 178303831d35Sstevel * (SBBC) mailbox message. The SBBC mailbox resides in 178403831d35Sstevel * IOSRAM. 178503831d35Sstevel * 178603831d35Sstevel * Note that this function is responsible for ensuring that 178703831d35Sstevel * a driver-specific representation of a mailbox <Value> is 178803831d35Sstevel * mapped into the representation that is expected by ScApp 178903831d35Sstevel * (eg, see LW8_WDT_PROP_RECOV). 179003831d35Sstevel */ 179103831d35Sstevel static int 179203831d35Sstevel ntwdt_set_cfgvar(int var, int val) 179303831d35Sstevel { 179403831d35Sstevel int rv; 179503831d35Sstevel int mbox_val; 179603831d35Sstevel lw8_set_wdt_t set_wdt; 179703831d35Sstevel 179803831d35Sstevel switch (var) { 179903831d35Sstevel case LW8_WDT_PROP_RECOV: 180003831d35Sstevel #ifdef DEBUG 180103831d35Sstevel NTWDT_DBG(WDT_DBG_PROT, ("MBOX_SET of 'recovery-enabled':" 180203831d35Sstevel " %s (%d)", (val != 0) ? "enabled" : "disabled", val)); 180303831d35Sstevel #endif 180403831d35Sstevel mbox_val = (val != 0) ? LW8_PROP_RECOV_ENABLED : 180503831d35Sstevel LW8_PROP_RECOV_DISABLED; 180603831d35Sstevel break; 180703831d35Sstevel 180803831d35Sstevel case LW8_WDT_PROP_WDT: 180903831d35Sstevel #ifdef DEBUG 181003831d35Sstevel NTWDT_DBG(WDT_DBG_PROT, ("MBOX_SET of 'wdog-enabled':" 181103831d35Sstevel " %s (%d)", (val != 0) ? "enabled" : "disabled", val)); 181203831d35Sstevel #endif 181303831d35Sstevel mbox_val = (val != 0) ? LW8_PROP_WDT_ENABLED : 181403831d35Sstevel LW8_PROP_WDT_DISABLED; 181503831d35Sstevel break; 181603831d35Sstevel 181703831d35Sstevel case LW8_WDT_PROP_TO: 181803831d35Sstevel #ifdef DEBUG 181903831d35Sstevel NTWDT_DBG(WDT_DBG_PROT, ("MBOX_SET of 'wdog-timeout':" 182003831d35Sstevel " %d seconds", val)); 182103831d35Sstevel #endif 182203831d35Sstevel mbox_val = val; 182303831d35Sstevel break; 182403831d35Sstevel 182503831d35Sstevel case LW8_WDT_PROP_MODE: 182603831d35Sstevel #ifdef DEBUG 182703831d35Sstevel NTWDT_DBG(WDT_DBG_PROT, ("MBOX_SET of 'wdog-mode':" 182803831d35Sstevel " %s (%d)", (val != LW8_PROP_MODE_SWDT) ? 182903831d35Sstevel "AWDT" : "SWDT", val)); 183003831d35Sstevel #endif 183103831d35Sstevel mbox_val = val; 183203831d35Sstevel break; 183303831d35Sstevel 183403831d35Sstevel default: 183503831d35Sstevel ASSERT(0); 183603831d35Sstevel _NOTE(NOTREACHED) 183703831d35Sstevel } 183803831d35Sstevel 183903831d35Sstevel set_wdt.property_id = var; 184003831d35Sstevel set_wdt.value = mbox_val; 184103831d35Sstevel 184203831d35Sstevel rv = ntwdt_lomcmd(LW8_MBOX_WDT_SET, (intptr_t)&set_wdt); 184303831d35Sstevel if (rv != 0) { 184403831d35Sstevel _NOTE(EMPTY) 184503831d35Sstevel NTWDT_DBG(WDT_DBG_PROT, ("MBOX_SET of prop/val %d/%d " 184603831d35Sstevel "failed: %d", var, mbox_val, rv)); 184703831d35Sstevel } 184803831d35Sstevel 184903831d35Sstevel return (rv); 185003831d35Sstevel } 185103831d35Sstevel 185203831d35Sstevel static void 185303831d35Sstevel ntwdt_set_cfgvar_noreply(int var, int val) 185403831d35Sstevel { 1855*07d06da5SSurya Prakki (void) ntwdt_set_cfgvar(var, val); 185603831d35Sstevel } 185703831d35Sstevel 185803831d35Sstevel #ifdef DEBUG 185903831d35Sstevel /* 186003831d35Sstevel * Read a specified WDT Property from ScApp. 186103831d35Sstevel * 186203831d35Sstevel * <Property> is passed in the Request of the LW8_MBOX_WDT_GET 186303831d35Sstevel * (SBBC) mailbox message, and the Property's <Value> 186403831d35Sstevel * is returned in the message's Response. The SBBC mailbox 186503831d35Sstevel * resides in IOSRAM. 186603831d35Sstevel */ 186703831d35Sstevel static int 186803831d35Sstevel ntwdt_get_cfgvar(int var, int *val) 186903831d35Sstevel { 187003831d35Sstevel lw8_get_wdt_t get_wdt; 187103831d35Sstevel int rv; 187203831d35Sstevel 187303831d35Sstevel rv = ntwdt_lomcmd(LW8_MBOX_WDT_GET, (intptr_t)&get_wdt); 187403831d35Sstevel if (rv != 0) { 187503831d35Sstevel _NOTE(EMPTY) 187603831d35Sstevel NTWDT_DBG(WDT_DBG_PROT, ("MBOX_GET failed: %d", rv)); 187703831d35Sstevel } else { 187803831d35Sstevel switch (var) { 187903831d35Sstevel case LW8_WDT_PROP_RECOV: 188003831d35Sstevel *val = (uint8_t)get_wdt.recovery_enabled; 188103831d35Sstevel NTWDT_DBG(WDT_DBG_PROT, ("MBOX_GET of 'reset-enabled':" 188203831d35Sstevel " %s (%d)", (*val != 0) ? "enabled" : "disabled", 188303831d35Sstevel *val)); 188403831d35Sstevel break; 188503831d35Sstevel 188603831d35Sstevel case LW8_WDT_PROP_WDT: 188703831d35Sstevel *val = (uint8_t)get_wdt.watchdog_enabled; 188803831d35Sstevel NTWDT_DBG(WDT_DBG_PROT, ("MBOX_GET of 'wdog-enabled':" 188903831d35Sstevel " %s (%d)", (*val != 0) ? "enabled" : "disabled", 189003831d35Sstevel *val)); 189103831d35Sstevel break; 189203831d35Sstevel 189303831d35Sstevel case LW8_WDT_PROP_TO: 189403831d35Sstevel *val = (uint8_t)get_wdt.timeout; 189503831d35Sstevel NTWDT_DBG(WDT_DBG_PROT, ("MBOX_GET of 'wdog-timeout':" 189603831d35Sstevel " %d seconds", *val)); 189703831d35Sstevel break; 189803831d35Sstevel 189903831d35Sstevel default: 190003831d35Sstevel ASSERT(0); 190103831d35Sstevel _NOTE(NOTREACHED) 190203831d35Sstevel } 190303831d35Sstevel } 190403831d35Sstevel 190503831d35Sstevel return (rv); 190603831d35Sstevel } 190703831d35Sstevel #endif 190803831d35Sstevel 190903831d35Sstevel /* 191003831d35Sstevel * Update the real system "heartbeat", which resides in IOSRAM. 191103831d35Sstevel * This "heartbeat" is normally used in SWDT Mode, but when 191203831d35Sstevel * in AWDT Mode, ScApp also uses its value to determine if Solaris 191303831d35Sstevel * is up-and-running. 191403831d35Sstevel */ 191503831d35Sstevel static void 191603831d35Sstevel ntwdt_pat_hw_watchdog() 191703831d35Sstevel { 191803831d35Sstevel tod_iosram_t tod_buf; 191903831d35Sstevel static uint32_t i_am_alive = 0; 192003831d35Sstevel #ifdef DEBUG 192103831d35Sstevel if (ntwdt_stop_heart != 0) 192203831d35Sstevel return; 192303831d35Sstevel #endif 192403831d35Sstevel /* Update the system heartbeat */ 192503831d35Sstevel if (i_am_alive == UINT32_MAX) 192603831d35Sstevel i_am_alive = 0; 192703831d35Sstevel else 192803831d35Sstevel i_am_alive++; 192903831d35Sstevel 193003831d35Sstevel NTWDT_DBG(WDT_DBG_HEART, ("update heartbeat: %d", 193103831d35Sstevel i_am_alive)); 193203831d35Sstevel 193303831d35Sstevel if (iosram_write(SBBC_TOD_KEY, OFFSET(tod_buf, tod_i_am_alive), 193403831d35Sstevel (char *)&i_am_alive, sizeof (uint32_t))) { 193503831d35Sstevel cmn_err(CE_WARN, "ntwdt_pat_hw_watchdog(): " 193603831d35Sstevel "write heartbeat failed"); 193703831d35Sstevel } 193803831d35Sstevel } 193903831d35Sstevel 194003831d35Sstevel /* 194103831d35Sstevel * Write the specified value to the system's normal (IOSRAM) 194203831d35Sstevel * location that's used to specify Solaris' watchdog-timeout 194303831d35Sstevel * on Serengeti platforms. 194403831d35Sstevel * 194503831d35Sstevel * In SWDT Mode, this location can hold values [0,n). 194603831d35Sstevel * In AWDT Mode, this location must have value 0 (else 194703831d35Sstevel * after a ScApp-reboot, ScApp could mistakenly interpret 194803831d35Sstevel * that the system is in SWDT Mode). 194903831d35Sstevel */ 195003831d35Sstevel static int 195103831d35Sstevel ntwdt_set_hw_timeout(uint32_t period) 195203831d35Sstevel { 195303831d35Sstevel tod_iosram_t tod_buf; 195403831d35Sstevel int rv; 195503831d35Sstevel 195603831d35Sstevel rv = iosram_write(SBBC_TOD_KEY, OFFSET(tod_buf, tod_timeout_period), 195703831d35Sstevel (char *)&period, sizeof (uint32_t)); 195803831d35Sstevel if (rv != 0) 195903831d35Sstevel cmn_err(CE_WARN, "write of %d for TOD timeout " 196003831d35Sstevel "period failed: %d", period, rv); 196103831d35Sstevel 196203831d35Sstevel return (rv); 196303831d35Sstevel } 196403831d35Sstevel 196503831d35Sstevel /* 196603831d35Sstevel * Soft-interrupt handler that is triggered when ScApp wants 196703831d35Sstevel * to know the current state of the app-wdog. 196803831d35Sstevel * 196903831d35Sstevel * Grab ntwdt_wdog_mutex so that we synchronize with any 197003831d35Sstevel * concurrent User Context and Interrupt Context activity. Call 197103831d35Sstevel * a function that writes a permutation of the watchdog state 197203831d35Sstevel * to the SC, then release the mutex. 197303831d35Sstevel * 197403831d35Sstevel * We grab the mutex not only so that each variable is consistent 197503831d35Sstevel * but also so that the *permutation* of variables is consistent. 197603831d35Sstevel * I.e., any set of one or more variables (that we write to SC 197703831d35Sstevel * using multiple mailbox commands) will truly be seen as a 197803831d35Sstevel * consistent snapshot. Note that if our protocol had a MBOX_SET 197903831d35Sstevel * command that allowed writing all watchdog state in one 198003831d35Sstevel * command, then the lock-hold latency would be greatly reduced. 198103831d35Sstevel * To our advantage, this softint normally executes very 198203831d35Sstevel * infrequently. 198303831d35Sstevel * 198403831d35Sstevel * Context: 198503831d35Sstevel * called at Interrupt Context (DDI_SOFTINT_LOW) 198603831d35Sstevel */ 198703831d35Sstevel static uint_t 198803831d35Sstevel ntwdt_mbox_softint(char *arg) 198903831d35Sstevel { 199003831d35Sstevel ntwdt_wdog_t *wdog_state; 199103831d35Sstevel 199203831d35Sstevel wdog_state = ((ntwdt_state_t *)arg)->ntwdt_wdog_state; 199303831d35Sstevel 199403831d35Sstevel ASSERT(wdog_state != NULL); 199503831d35Sstevel 199603831d35Sstevel mutex_enter(&wdog_state->ntwdt_wdog_mutex); 199703831d35Sstevel 199803831d35Sstevel /* tell ScApp state of AWDT */ 1999*07d06da5SSurya Prakki (void) ntwdt_set_awdt_state(wdog_state); 200003831d35Sstevel 200103831d35Sstevel mutex_exit(&wdog_state->ntwdt_wdog_mutex); 200203831d35Sstevel 200303831d35Sstevel return (DDI_INTR_CLAIMED); 200403831d35Sstevel } 200503831d35Sstevel 200603831d35Sstevel /* 200703831d35Sstevel * Handle MBOX_EVENT_LW8 Events that are sent from ScApp. 200803831d35Sstevel * 200903831d35Sstevel * The only (sub-)type of Event we handle is the 201003831d35Sstevel * LW8_EVENT_SC_RESTARTED Event. We handle this by triggering 201103831d35Sstevel * a soft-interrupt only if we are in AWDT mode. 201203831d35Sstevel * 201303831d35Sstevel * ScApp sends this Event when it wants to learn the current 201403831d35Sstevel * state of the AWDT variables. Design-wise, this is used to 201503831d35Sstevel * handle the case where the SC reboots while the system is in 201603831d35Sstevel * AWDT mode (if the SC reboots in SWDT mode, then ScApp 201703831d35Sstevel * already knows all necessary info and therefore won't send 201803831d35Sstevel * this Event). 201903831d35Sstevel * 202003831d35Sstevel * Context: 202103831d35Sstevel * function is called in Interrupt Context (at DDI_SOFTINT_MED) 202203831d35Sstevel * and we conditionally trigger a softint that will run at 202303831d35Sstevel * DDI_SOFTINT_LOW. Note that function executes at 202403831d35Sstevel * DDI_SOFTINT_MED due to how this handler was registered by 202503831d35Sstevel * the implementation of sbbc_mbox_reg_intr(). 202603831d35Sstevel * 202703831d35Sstevel * Notes: 202803831d35Sstevel * Currently, the LW8_EVENT_SC_RESTARTED Event is only sent 202903831d35Sstevel * by SC when in AWDT mode. 203003831d35Sstevel */ 203103831d35Sstevel static uint_t 203203831d35Sstevel ntwdt_event_data_handler(char *arg) 203303831d35Sstevel { 203403831d35Sstevel lw8_event_t *payload; 203503831d35Sstevel sbbc_msg_t *msg; 203603831d35Sstevel 203703831d35Sstevel if (arg == NULL) { 203803831d35Sstevel return (DDI_INTR_CLAIMED); 203903831d35Sstevel } 204003831d35Sstevel 204103831d35Sstevel msg = (sbbc_msg_t *)arg; 204203831d35Sstevel if (msg->msg_buf == NULL) { 204303831d35Sstevel return (DDI_INTR_CLAIMED); 204403831d35Sstevel } 204503831d35Sstevel 204603831d35Sstevel payload = (lw8_event_t *)msg->msg_buf; 204703831d35Sstevel 204803831d35Sstevel switch (payload->event_type) { 204903831d35Sstevel case LW8_EVENT_SC_RESTARTED: 205003831d35Sstevel /* 205103831d35Sstevel * then SC probably was rebooted, and it therefore 205203831d35Sstevel * needs to know what the current state of AWDT is. 205303831d35Sstevel */ 205403831d35Sstevel NTWDT_DBG(WDT_DBG_EVENT, ("LW8_EVENT_SC_RESTARTED " 205503831d35Sstevel "received in %s mode", 205603831d35Sstevel (ntwdt_watchdog_activated != 0) ? "AWDT" : "SWDT")); 205703831d35Sstevel 205803831d35Sstevel if (ntwdt_watchdog_activated != 0) { 205903831d35Sstevel /* then system is in AWDT mode */ 206003831d35Sstevel ddi_trigger_softintr(ntwdt_mbox_softint_id); 206103831d35Sstevel } 206203831d35Sstevel break; 206303831d35Sstevel 206403831d35Sstevel default: 206503831d35Sstevel NTWDT_DBG(WDT_DBG_EVENT, 206603831d35Sstevel ("MBOX_EVENT_LW8: %d", payload->event_type)); 206703831d35Sstevel break; 206803831d35Sstevel } 206903831d35Sstevel 207003831d35Sstevel return (DDI_INTR_CLAIMED); 207103831d35Sstevel } 207203831d35Sstevel 207303831d35Sstevel /* 207403831d35Sstevel * Send an SBBC Mailbox command to ScApp. 207503831d35Sstevel * 207603831d35Sstevel * Use the sbbc_mbox_request_response utility function to 207703831d35Sstevel * send the Request and receive the optional Response. 207803831d35Sstevel * 207903831d35Sstevel * Context: 208003831d35Sstevel * can be called from Interrupt Context or User Context. 208103831d35Sstevel */ 208203831d35Sstevel static int 208303831d35Sstevel ntwdt_lomcmd(int cmd, intptr_t arg) 208403831d35Sstevel { 208503831d35Sstevel sbbc_msg_t request; 208603831d35Sstevel sbbc_msg_t *reqp; 208703831d35Sstevel sbbc_msg_t response; 208803831d35Sstevel sbbc_msg_t *resp; 208903831d35Sstevel int rv = 0; 209003831d35Sstevel 209103831d35Sstevel reqp = &request; 209203831d35Sstevel bzero((caddr_t)&request, sizeof (request)); 209303831d35Sstevel reqp->msg_type.type = LW8_MBOX; 209403831d35Sstevel reqp->msg_type.sub_type = (uint16_t)cmd; 209503831d35Sstevel 209603831d35Sstevel resp = &response; 209703831d35Sstevel bzero((caddr_t)&response, sizeof (response)); 209803831d35Sstevel resp->msg_type.type = LW8_MBOX; 209903831d35Sstevel resp->msg_type.sub_type = (uint16_t)cmd; 210003831d35Sstevel 210103831d35Sstevel switch (cmd) { 210203831d35Sstevel case LW8_MBOX_WDT_GET: 210303831d35Sstevel reqp->msg_len = 0; 210403831d35Sstevel reqp->msg_buf = (caddr_t)NULL; 210503831d35Sstevel resp->msg_len = sizeof (lw8_get_wdt_t); 210603831d35Sstevel resp->msg_buf = (caddr_t)arg; 210703831d35Sstevel break; 210803831d35Sstevel 210903831d35Sstevel case LW8_MBOX_WDT_SET: 211003831d35Sstevel reqp->msg_len = sizeof (lw8_set_wdt_t); 211103831d35Sstevel reqp->msg_buf = (caddr_t)arg; 211203831d35Sstevel resp->msg_len = 0; 211303831d35Sstevel resp->msg_buf = (caddr_t)NULL; 211403831d35Sstevel break; 211503831d35Sstevel 211603831d35Sstevel default: 211703831d35Sstevel return (EINVAL); 211803831d35Sstevel } 211903831d35Sstevel 212003831d35Sstevel rv = sbbc_mbox_request_response(reqp, resp, 212103831d35Sstevel LW8_DEFAULT_MAX_MBOX_WAIT_TIME); 212203831d35Sstevel 212303831d35Sstevel if ((rv) || (resp->msg_status != SG_MBOX_STATUS_SUCCESS)) { 212403831d35Sstevel 212503831d35Sstevel NTWDT_NDBG(WDT_DBG_PROT, ("SBBC mailbox error:" 212603831d35Sstevel " (rv/msg_status)=(%d/%d)", rv, resp->msg_status)); 212703831d35Sstevel 212803831d35Sstevel /* errors from sgsbbc */ 212903831d35Sstevel if (resp->msg_status > 0) { 213003831d35Sstevel return (resp->msg_status); 213103831d35Sstevel } 213203831d35Sstevel 213303831d35Sstevel /* errors from ScApp */ 213403831d35Sstevel switch (resp->msg_status) { 213503831d35Sstevel case SG_MBOX_STATUS_ILLEGAL_PARAMETER: 213603831d35Sstevel /* illegal ioctl parameter */ 213703831d35Sstevel return (EINVAL); 213803831d35Sstevel 213903831d35Sstevel default: 214003831d35Sstevel return (EIO); 214103831d35Sstevel } 214203831d35Sstevel } 214303831d35Sstevel return (0); 214403831d35Sstevel } 2145