github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/pkg/watchdog/watchdog.go (about) 1 // Copyright 2021 the u-root Authors. All rights reserved 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package watchdog provides functions for interacting with the Linux watchdog. 6 // 7 // The basic usage is: 8 // 9 // wd, err := watchdog.Open(watchdog.Dev) 10 // while running { 11 // wd.KeepAlive() 12 // } 13 // wd.MagicClose() 14 // 15 // Open() arms the watchdog. MagicClose() disarms the watchdog. 16 // 17 // Note not every watchdog driver supports every function! 18 // 19 // For more, see: 20 // https://www.kernel.org/doc/Documentation/watchdog/watchdog-api.txt 21 package watchdog 22 23 import ( 24 "fmt" 25 "os" 26 "time" 27 "unsafe" 28 29 "golang.org/x/sys/unix" 30 ) 31 32 // Dev is the name of the first watchdog. If there are multiple watchdogs, they 33 // are named /dev/watchdog0, /dev/watchdog1, ... 34 const Dev = "/dev/watchdog" 35 36 // Various ioctl numbers. 37 const ( 38 wdiocGetSupport = 0x80285700 39 wdiocGetStatus = 0x80045701 40 wdiocGetBootStatus = 0x80045702 41 wdiocGetTemp = 0x80045703 42 wdiocSetOptions = 0x80045704 43 wdiocKeepAlive = 0x80045705 44 wdiocSetTimeout = 0xc0045706 45 wdiocGetTimeout = 0x80045707 46 wdiocSetPreTimeout = 0xc0045708 47 wdiocGetPreTimeout = 0x80045709 48 wdiocGetTimeLeft = 0x8004570a 49 ) 50 51 // Status contains flags returned by Status() and BootStatus(). These are the 52 // same flags used for Support()'s options field. 53 type Status int32 54 55 // Bitset of possible flags for the Status() type. 56 const ( 57 // Unknown flag error 58 StatusUnknown Status = -1 59 // Reset due to CPU overheat 60 StatusOverheat Status = 0x0001 61 // Fan failed 62 StatusFanFault Status = 0x0002 63 // External relay 1 64 StatusExtern1 Status = 0x0004 65 // ExStatusl relay 2 66 StatusExtern2 Status = 0x0008 67 // Power bad/power fault 68 StatusPowerUnder Status = 0x0010 69 // Card previously reset the CPU 70 StatusCardReset Status = 0x0020 71 // Power over voltage 72 StatusPowerOver Status = 0x0040 73 // Set timeout (in seconds) 74 StatusSetTimeout Status = 0x0080 75 // Supports magic close char 76 StatusMagicClose Status = 0x0100 77 // Pretimeout (in seconds), get/set 78 StatusPreTimeout Status = 0x0200 79 // Watchdog triggers a management or other external alarm not a reboot 80 StatusAlarmOnly Status = 0x0400 81 // Keep alive ping reply 82 StatusKeepAlivePing Status = 0x8000 83 ) 84 85 // Option are options passed to SetOptions(). 86 type Option int32 87 88 // Bitset of possible flags for the Option type. 89 const ( 90 // Unknown status error 91 OptionUnknown Option = -1 92 // Turn off the watchdog timer 93 OptionDisableCard Option = 0x0001 94 // Turn on the watchdog timer 95 OptionEnableCard Option = 0x0002 96 // Kernel panic on temperature trip 97 OptionTempPanic Option = 0x0004 98 ) 99 100 type syscalls interface { 101 unixSyscall(uintptr, uintptr, uintptr, unsafe.Pointer) (uintptr, uintptr, unix.Errno) 102 unixIoctlGetUint32(int, uint) (uint32, error) 103 } 104 105 // Watchdog holds the descriptor of an open watchdog driver. 106 type Watchdog struct { 107 *os.File 108 syscalls 109 } 110 111 type realSyscalls struct{} 112 113 func (r *realSyscalls) unixSyscall(trap, a1, a2 uintptr, a3 unsafe.Pointer) (uintptr, uintptr, unix.Errno) { 114 return unix.Syscall(trap, a1, a2, uintptr(a3)) 115 } 116 117 func (r *realSyscalls) unixIoctlGetUint32(fd int, req uint) (uint32, error) { 118 return unix.IoctlGetUint32(fd, req) 119 } 120 121 // Open arms the watchdog. 122 func Open(dev string) (*Watchdog, error) { 123 f, err := os.OpenFile(dev, os.O_RDWR, 0) 124 if err != nil { 125 return nil, err 126 } 127 return &Watchdog{ 128 File: f, 129 syscalls: &realSyscalls{}, 130 }, 131 nil 132 } 133 134 // Close closes the device without disarming the watchdog. 135 func (w *Watchdog) Close() error { 136 return w.File.Close() 137 } 138 139 // MagicClose disarms the watchdog. However if the kernel is compiled with 140 // CONFIG_WATCHDOG_NOWAYOUT=y, there may be no way to disarm the watchdog. 141 func (w *Watchdog) MagicClose() error { 142 if _, err := w.File.Write([]byte("V")); err != nil { 143 w.File.Close() 144 return err 145 } 146 return w.File.Close() 147 } 148 149 // Support returns the WatchdogInfo struct. 150 func (w *Watchdog) Support() (*unix.WatchdogInfo, error) { 151 var wi unix.WatchdogInfo 152 if _, _, err := w.unixSyscall(unix.SYS_IOCTL, w.File.Fd(), wdiocGetSupport, unsafe.Pointer(&wi)); err != 0 { 153 return nil, err 154 } 155 return &wi, nil 156 } 157 158 // Status returns the current status. 159 func (w *Watchdog) Status() (Status, error) { 160 flags, err := w.unixIoctlGetUint32(int(w.File.Fd()), wdiocGetStatus) 161 if err != nil { 162 return StatusUnknown, err 163 } 164 return Status(flags), nil 165 } 166 167 // BootStatus returns the status at the last reboot. 168 func (w *Watchdog) BootStatus() (Status, error) { 169 flags, err := w.unixIoctlGetUint32(int(w.File.Fd()), wdiocGetBootStatus) 170 if err != nil { 171 return StatusUnknown, err 172 } 173 return Status(flags), nil 174 } 175 176 // SetOptions can be used to control some aspects of the cards operation. 177 func (w *Watchdog) SetOptions(options Option) error { 178 if _, _, err := w.unixSyscall(unix.SYS_IOCTL, w.File.Fd(), wdiocSetOptions, unsafe.Pointer(&options)); err != 0 { 179 return err 180 } 181 return nil 182 } 183 184 // KeepAlive pets the watchdog. 185 func (w *Watchdog) KeepAlive() error { 186 _, err := w.File.WriteString("1") 187 return err 188 } 189 190 // SetTimeout sets the watchdog timeout on the fly. It returns an error if the 191 // timeout gets set to the wrong value. timeout must be a multiple of seconds; 192 // otherwise, an error is returned. 193 func (w *Watchdog) SetTimeout(timeout time.Duration) error { 194 to := timeout / time.Second 195 if _, _, err := w.unixSyscall(unix.SYS_IOCTL, w.File.Fd(), wdiocSetTimeout, unsafe.Pointer(&timeout)); err != 0 { 196 return err 197 } 198 gotTimeout := to * time.Second 199 if gotTimeout != timeout { 200 return fmt.Errorf("Watchdog timeout set to %v, wanted %v", gotTimeout, timeout) 201 } 202 return nil 203 } 204 205 // Timeout returns the current watchdog timeout. 206 func (w *Watchdog) Timeout() (time.Duration, error) { 207 timeout, err := w.unixIoctlGetUint32(int(w.File.Fd()), wdiocGetTimeout) 208 if err != nil { 209 return 0, err 210 } 211 return time.Duration(timeout) * time.Second, nil 212 } 213 214 // SetPreTimeout sets the watchdog pretimeout on the fly. The pretimeout is the 215 // duration before triggering the preaction (such as an NMI, interrupt, ...). 216 // timeout must be a multiple of seconds; otherwise, an error is returned. 217 func (w *Watchdog) SetPreTimeout(timeout time.Duration) error { 218 to := timeout / time.Second 219 if _, _, err := w.unixSyscall(unix.SYS_IOCTL, w.File.Fd(), wdiocSetPreTimeout, unsafe.Pointer(&timeout)); err != 0 { 220 return err 221 } 222 gotTimeout := to * time.Second 223 if gotTimeout != timeout { 224 return fmt.Errorf("Watchdog pretimeout set to %v, wanted %v", gotTimeout, timeout) 225 } 226 return nil 227 } 228 229 // PreTimeout returns the current watchdog pretimeout. 230 func (w *Watchdog) PreTimeout() (time.Duration, error) { 231 timeout, err := w.unixIoctlGetUint32(int(w.File.Fd()), wdiocGetPreTimeout) 232 if err != nil { 233 return 0, err 234 } 235 return time.Duration(timeout) * time.Second, nil 236 } 237 238 // TimeLeft returns the duration before the reboot (to the nearest second). 239 func (w *Watchdog) TimeLeft() (time.Duration, error) { 240 left, err := w.unixIoctlGetUint32(int(w.File.Fd()), wdiocGetTimeLeft) 241 if err != nil { 242 return 0, err 243 } 244 return time.Duration(left) * time.Second, nil 245 }