1 // Copyright 2016 The Fuchsia Authors
2 // Copyright (c) 2013 Google Inc.
3 // Copyright (c) 2015 Travis Geiselbrecht
4 //
5 // Use of this source code is governed by a MIT-style
6 // license that can be found in the LICENSE file or at
7 // https://opensource.org/licenses/MIT
8
9 #include <lib/watchdog.h>
10
11 #include <assert.h>
12 #include <err.h>
13 #include <inttypes.h>
14 #include <kernel/auto_lock.h>
15 #include <kernel/spinlock.h>
16 #include <kernel/thread.h>
17 #include <kernel/timer.h>
18 #include <platform.h>
19 #include <zircon/compiler.h>
20 #include <zircon/time.h>
21 #include <zircon/types.h>
22
23 static SpinLock lock;
24
watchdog_handler(watchdog_t * dog)25 __WEAK void watchdog_handler(watchdog_t* dog) {
26 dprintf(INFO, "Watchdog \"%s\" (timeout %" PRId64 " mSec) just fired!!\n",
27 dog->name, dog->timeout / (1000 * 1000));
28 platform_halt(HALT_ACTION_HALT, HALT_REASON_SW_RESET);
29 }
30
watchdog_timer_callback(timer_t * timer,zx_time_t now,void * arg)31 static void watchdog_timer_callback(timer_t* timer, zx_time_t now, void* arg) {
32 watchdog_handler((watchdog_t*)arg);
33
34 /* We should never get here; watchdog handlers should always be fatal. */
35 DEBUG_ASSERT(false);
36 }
37
watchdog_init(watchdog_t * dog,zx_duration_t timeout,const char * name)38 zx_status_t watchdog_init(watchdog_t* dog, zx_duration_t timeout, const char* name) {
39 DEBUG_ASSERT(NULL != dog);
40 DEBUG_ASSERT(ZX_TIME_INFINITE != timeout);
41
42 dog->magic = WATCHDOG_MAGIC;
43 dog->name = name ? name : "unnamed watchdog";
44 dog->enabled = false;
45 dog->timeout = timeout;
46 timer_init(&dog->expire_timer);
47
48 return ZX_OK;
49 }
50
watchdog_set_enabled(watchdog_t * dog,bool enabled)51 void watchdog_set_enabled(watchdog_t* dog, bool enabled) {
52 AutoSpinLock guard(&lock);
53
54 DEBUG_ASSERT((NULL != dog) && (WATCHDOG_MAGIC == dog->magic));
55
56 if (dog->enabled == enabled) {
57 return;
58 }
59
60 dog->enabled = enabled;
61 zx_time_t deadline = zx_time_add_duration(current_time(), dog->timeout);
62 if (enabled) {
63 timer_set_oneshot(&dog->expire_timer, deadline, watchdog_timer_callback, dog);
64 } else {
65 timer_cancel(&dog->expire_timer);
66 }
67 }
68
watchdog_pet(watchdog_t * dog)69 void watchdog_pet(watchdog_t* dog) {
70 AutoSpinLock guard(&lock);
71
72 DEBUG_ASSERT((NULL != dog) && (WATCHDOG_MAGIC == dog->magic));
73
74 if (!dog->enabled) {
75 return;
76 }
77
78 timer_cancel(&dog->expire_timer);
79 zx_time_t deadline = zx_time_add_duration(current_time(), dog->timeout);
80 timer_set_oneshot(&dog->expire_timer, deadline, watchdog_timer_callback, dog);
81 }
82
83 static timer_t hw_watchdog_timer;
84 static bool hw_watchdog_enabled;
85 static zx_duration_t hw_watchdog_pet_timeout;
86
hw_watchdog_timer_callback(timer_t * timer,zx_time_t now,void * arg)87 static void hw_watchdog_timer_callback(timer_t* timer, zx_time_t now, void* arg) {
88 timer_set_oneshot(timer,
89 zx_time_add_duration(now, hw_watchdog_pet_timeout),
90 hw_watchdog_timer_callback, NULL);
91 platform_watchdog_pet();
92 }
93
watchdog_hw_init(zx_time_t timeout)94 zx_status_t watchdog_hw_init(zx_time_t timeout) {
95 DEBUG_ASSERT(ZX_TIME_INFINITE != timeout);
96 timer_init(&hw_watchdog_timer);
97 return platform_watchdog_init(timeout, &hw_watchdog_pet_timeout);
98 }
99
watchdog_hw_set_enabled(bool enabled)100 void watchdog_hw_set_enabled(bool enabled) {
101 AutoSpinLock guard(&lock);
102
103 if (hw_watchdog_enabled == enabled) {
104 return;
105 }
106
107 hw_watchdog_enabled = enabled;
108 platform_watchdog_set_enabled(enabled);
109 if (enabled) {
110 timer_set_oneshot(&hw_watchdog_timer,
111 zx_time_add_duration(current_time(), hw_watchdog_pet_timeout),
112 hw_watchdog_timer_callback, NULL);
113 } else {
114 timer_cancel(&hw_watchdog_timer);
115 }
116 }
117