xref: /illumos-gate/usr/src/uts/sun4u/lw8/io/ntwdt.c (revision d1d6926f)
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 /*
2307d06da5SSurya 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 = {
487*d1d6926fSToomas Soome 	&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
_init(void)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
_info(struct modinfo * modinfop)55303831d35Sstevel _info(struct modinfo *modinfop)
55403831d35Sstevel {
55503831d35Sstevel 	NTWDT_DBG(WDT_DBG_ENTRY, ("_info"));
55603831d35Sstevel 
55703831d35Sstevel 	return (mod_info(&modlinkage, modinfop));
55803831d35Sstevel }
55903831d35Sstevel 
56003831d35Sstevel int
_fini(void)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
ntwdt_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)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",
58407d06da5SSurya 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 */
60207d06da5SSurya 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,
678*d1d6926fSToomas Soome 	    DDI_PSEUDO, 0) == 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:
69307d06da5SSurya 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
ntwdt_chk_wdog_support()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
ntwdt_chk_sc_support()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
ntwdt_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)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",
79607d06da5SSurya 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 
81607d06da5SSurya Prakki 		ntwdt_remove_callbacks();
81703831d35Sstevel 
81803831d35Sstevel 		ddi_remove_softintr(ntwdt_cyclic_softint_id);
81903831d35Sstevel 
82007d06da5SSurya 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
ntwdt_add_mbox_handlers(ntwdt_state_t * ntwdt_ptr)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
ntwdt_remove_mbox_handlers(void)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
ntwdt_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)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
ntwdt_open(dev_t * devp,int flag,int otyp,cred_t * credp)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",
97907d06da5SSurya 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
ntwdt_close(dev_t dev,int flag,int otyp,cred_t * credp)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",
102507d06da5SSurya 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
ntwdt_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)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 */
117807d06da5SSurya 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 */
118307d06da5SSurya Prakki 		(void) ntwdt_set_cfgvar(LW8_WDT_PROP_WDT,
118403831d35Sstevel 		    wdog_state->ntwdt_wdog_enabled);
118507d06da5SSurya 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 			 */
120007d06da5SSurya 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 */
124607d06da5SSurya 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
ntwdt_arm_vwdt(ntwdt_wdog_t * wdog_state)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
ntwdt_swdt_to_awdt(ntwdt_wdog_t * wdog_state)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
ntwdt_cyclic_pat(void * arg)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
ntwdt_cyclic_softint(char * arg)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 */
150907d06da5SSurya 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
ntwdt_reprogram_wd(ntwdt_state_t * ntwdt_ptr)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)
154607d06da5SSurya 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
ntwdt_panic_cb(void * arg,int code)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
ntwdt_start_timer(ntwdt_state_t * ntwdt_ptr)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
ntwdt_stop_timer(void * arg)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
ntwdt_stop_timer_lock(void * arg)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
ntwdt_add_callbacks(ntwdt_state_t * ntwdt_ptr)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
ntwdt_remove_callbacks()166203831d35Sstevel ntwdt_remove_callbacks()
166303831d35Sstevel {
166407d06da5SSurya 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
ntwdt_enforce_timeout()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
ntwdt_read_props(ntwdt_state_t * ntwdt_ptr)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
ntwdt_set_swdt_state()174003831d35Sstevel ntwdt_set_swdt_state()
174103831d35Sstevel {
174203831d35Sstevel 	/*
174303831d35Sstevel 	 * note that ScApp only needs this one
174403831d35Sstevel 	 * variable when system is in SWDT mode.
174503831d35Sstevel 	 */
174607d06da5SSurya 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
ntwdt_set_awdt_state(ntwdt_wdog_t * rstatep)176403831d35Sstevel ntwdt_set_awdt_state(ntwdt_wdog_t *rstatep)
176503831d35Sstevel {
176603831d35Sstevel 	/* ScApp expects values in this order: */
176707d06da5SSurya Prakki 	(void) ntwdt_set_cfgvar(LW8_WDT_PROP_MODE,
176803831d35Sstevel 	    ntwdt_watchdog_activated != 0);
176907d06da5SSurya Prakki 	(void) ntwdt_set_cfgvar(LW8_WDT_PROP_TO,
177003831d35Sstevel 	    rstatep->ntwdt_wdog_timeout);
177107d06da5SSurya Prakki 	(void) ntwdt_set_cfgvar(LW8_WDT_PROP_RECOV,
177203831d35Sstevel 	    rstatep->ntwdt_reset_enabled);
177307d06da5SSurya 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
ntwdt_set_cfgvar(int var,int val)179203831d35Sstevel ntwdt_set_cfgvar(int var, int val)
179303831d35Sstevel {
1794*d1d6926fSToomas Soome 	int		rv;
1795*d1d6926fSToomas Soome 	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
ntwdt_set_cfgvar_noreply(int var,int val)185303831d35Sstevel ntwdt_set_cfgvar_noreply(int var, int val)
185403831d35Sstevel {
185507d06da5SSurya 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
ntwdt_get_cfgvar(int var,int * val)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
ntwdt_pat_hw_watchdog()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),
1934f500b196SRichard Bean 	    (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
ntwdt_set_hw_timeout(uint32_t period)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
ntwdt_mbox_softint(char * arg)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 */
199907d06da5SSurya 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
ntwdt_event_data_handler(char * arg)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
ntwdt_lomcmd(int cmd,intptr_t arg)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,
2121f500b196SRichard Bean 	    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