blob: e92cf818dac8c434e5dd4c8546a35dcf114ab06b [file] [log] [blame]
Patrick Venture5e6ac712017-10-25 12:16:19 -07001#include "watchdog.hpp"
2
Patrick Venture0b02be92018-08-31 11:55:55 -07003#include "watchdog_service.hpp"
4
William A. Kennington III52575252018-02-09 15:54:56 -08005#include <endian.h>
Patrick Venture0b02be92018-08-31 11:55:55 -07006
Yong Lia729bf42019-10-14 12:42:10 +08007#include <bitset>
Patrick Venture0b02be92018-08-31 11:55:55 -07008#include <cstdint>
Vernon Mauerye08fbff2019-04-03 09:19:34 -07009#include <ipmid/api.hpp>
William A. Kennington III021b4c12018-05-10 11:12:51 -070010#include <phosphor-logging/elog-errors.hpp>
Patrick Venture0b02be92018-08-31 11:55:55 -070011#include <phosphor-logging/elog.hpp>
William A. Kennington III52575252018-02-09 15:54:56 -080012#include <phosphor-logging/log.hpp>
13#include <string>
William A. Kennington III021b4c12018-05-10 11:12:51 -070014#include <xyz/openbmc_project/Common/error.hpp>
Patrick Venture894571d2017-11-09 14:46:54 -080015
William A. Kennington IIIbae471c2018-06-15 10:38:01 -070016using phosphor::logging::commit;
William A. Kennington III52575252018-02-09 15:54:56 -080017using phosphor::logging::level;
18using phosphor::logging::log;
William A. Kennington III021b4c12018-05-10 11:12:51 -070019using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
Patrick Venture5e6ac712017-10-25 12:16:19 -070020
William A. Kennington IIIbae471c2018-06-15 10:38:01 -070021static bool lastCallSuccessful = false;
22
23void reportError()
24{
25 // We don't want to fill the SEL with errors if the daemon dies and doesn't
26 // come back but the watchdog keeps on ticking. Instead, we only report the
27 // error if we haven't reported one since the last successful call
28 if (!lastCallSuccessful)
29 {
30 return;
31 }
32 lastCallSuccessful = false;
33
34 // TODO: This slow down the end of the IPMI transaction waiting
35 // for the commit to finish. commit<>() can take at least 5 seconds
36 // to complete. 5s is very slow for an IPMI command and ends up
37 // congesting the IPMI channel needlessly, especially if the watchdog
38 // is ticking fairly quickly and we have some transient issues.
39 commit<InternalFailure>();
40}
41
Vernon Mauery11df4f62019-03-25 14:17:54 -070042ipmi::RspType<> ipmiAppResetWatchdogTimer()
Patrick Venture5e6ac712017-10-25 12:16:19 -070043{
William A. Kennington III52575252018-02-09 15:54:56 -080044 try
45 {
46 WatchdogService wd_service;
William A. Kennington III52575252018-02-09 15:54:56 -080047
William A. Kennington IIIde14a022018-02-09 16:11:18 -080048 // Notify the caller if we haven't initialized our timer yet
49 // so it can configure actions and timeouts
William A. Kennington III2ecf5122018-04-27 14:31:51 -070050 if (!wd_service.getInitialized())
William A. Kennington IIIde14a022018-02-09 16:11:18 -080051 {
William A. Kennington IIIbae471c2018-06-15 10:38:01 -070052 lastCallSuccessful = true;
Vernon Mauery11df4f62019-03-25 14:17:54 -070053
54 constexpr uint8_t ccWatchdogNotInit = 0x80;
55 return ipmi::response(ccWatchdogNotInit);
William A. Kennington IIIde14a022018-02-09 16:11:18 -080056 }
57
William A. Kennington III4b017a92018-04-27 14:31:08 -070058 // The ipmi standard dictates we enable the watchdog during reset
59 wd_service.resetTimeRemaining(true);
William A. Kennington IIIbae471c2018-06-15 10:38:01 -070060 lastCallSuccessful = true;
Vernon Mauery11df4f62019-03-25 14:17:54 -070061 return ipmi::responseSuccess();
Patrick Venture5e6ac712017-10-25 12:16:19 -070062 }
William A. Kennington III021b4c12018-05-10 11:12:51 -070063 catch (const InternalFailure& e)
64 {
William A. Kennington IIIbae471c2018-06-15 10:38:01 -070065 reportError();
Vernon Mauery11df4f62019-03-25 14:17:54 -070066 return ipmi::responseUnspecifiedError();
William A. Kennington III021b4c12018-05-10 11:12:51 -070067 }
William A. Kennington III52575252018-02-09 15:54:56 -080068 catch (const std::exception& e)
69 {
70 const std::string e_str = std::string("wd_reset: ") + e.what();
71 log<level::ERR>(e_str.c_str());
Vernon Mauery11df4f62019-03-25 14:17:54 -070072 return ipmi::responseUnspecifiedError();
William A. Kennington III5325f2c2018-01-08 15:17:09 -080073 }
William A. Kennington III52575252018-02-09 15:54:56 -080074 catch (...)
75 {
76 log<level::ERR>("wd_reset: Unknown Error");
Vernon Mauery11df4f62019-03-25 14:17:54 -070077 return ipmi::responseUnspecifiedError();
William A. Kennington III5325f2c2018-01-08 15:17:09 -080078 }
Patrick Venture5e6ac712017-10-25 12:16:19 -070079}
William A. Kennington III61d5f7b2018-02-09 15:23:53 -080080
William A. Kennington III52575252018-02-09 15:54:56 -080081static constexpr uint8_t wd_dont_stop = 0x1 << 6;
82static constexpr uint8_t wd_timeout_action_mask = 0x3;
83
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +000084static constexpr uint8_t wdTimerUseResTimer1 = 0x0;
85static constexpr uint8_t wdTimerUseResTimer2 = 0x6;
86static constexpr uint8_t wdTimerUseResTimer3 = 0x7;
87
Yong Li4dd71af2019-09-29 14:18:07 +080088static constexpr uint8_t wdTimeoutActionMax = 3;
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +000089static constexpr uint8_t wdTimeoutInterruptTimer = 0x04;
Yong Li118907e2019-01-11 17:36:17 +080090
Patrick Venture0b02be92018-08-31 11:55:55 -070091enum class IpmiAction : uint8_t
92{
William A. Kennington III52575252018-02-09 15:54:56 -080093 None = 0x0,
94 HardReset = 0x1,
95 PowerOff = 0x2,
96 PowerCycle = 0x3,
97};
98
William A. Kennington IIIb638de22018-02-09 16:12:53 -080099/** @brief Converts an IPMI Watchdog Action to DBUS defined action
100 * @param[in] ipmi_action The IPMI Watchdog Action
101 * @return The Watchdog Action that the ipmi_action maps to
102 */
103WatchdogService::Action ipmiActionToWdAction(IpmiAction ipmi_action)
104{
Patrick Venture0b02be92018-08-31 11:55:55 -0700105 switch (ipmi_action)
William A. Kennington IIIb638de22018-02-09 16:12:53 -0800106 {
107 case IpmiAction::None:
108 {
109 return WatchdogService::Action::None;
110 }
111 case IpmiAction::HardReset:
112 {
113 return WatchdogService::Action::HardReset;
114 }
115 case IpmiAction::PowerOff:
116 {
117 return WatchdogService::Action::PowerOff;
118 }
119 case IpmiAction::PowerCycle:
120 {
121 return WatchdogService::Action::PowerCycle;
122 }
123 default:
124 {
125 throw std::domain_error("IPMI Action is invalid");
126 }
127 }
128}
129
Yong Li118907e2019-01-11 17:36:17 +0800130enum class IpmiTimerUse : uint8_t
131{
132 Reserved = 0x0,
133 BIOSFRB2 = 0x1,
134 BIOSPOST = 0x2,
135 OSLoad = 0x3,
136 SMSOS = 0x4,
137 OEM = 0x5,
138};
139
140WatchdogService::TimerUse ipmiTimerUseToWdTimerUse(IpmiTimerUse ipmiTimerUse)
141{
142 switch (ipmiTimerUse)
143 {
144 case IpmiTimerUse::Reserved:
145 {
146 return WatchdogService::TimerUse::Reserved;
147 }
148 case IpmiTimerUse::BIOSFRB2:
149 {
150 return WatchdogService::TimerUse::BIOSFRB2;
151 }
152 case IpmiTimerUse::BIOSPOST:
153 {
154 return WatchdogService::TimerUse::BIOSPOST;
155 }
156 case IpmiTimerUse::OSLoad:
157 {
158 return WatchdogService::TimerUse::OSLoad;
159 }
160 case IpmiTimerUse::SMSOS:
161 {
162 return WatchdogService::TimerUse::SMSOS;
163 }
164 case IpmiTimerUse::OEM:
165 {
166 return WatchdogService::TimerUse::OEM;
167 }
168 default:
169 {
170 return WatchdogService::TimerUse::Reserved;
171 }
172 }
173}
174
Yong Li4dd71af2019-09-29 14:18:07 +0800175static bool timerNotLogFlags = false;
Yong Lia729bf42019-10-14 12:42:10 +0800176static std::bitset<8> timerUseExpirationFlags = 0;
Yong Li4dd71af2019-09-29 14:18:07 +0800177static uint3_t timerPreTimeoutInterrupt = 0;
Yong Lia729bf42019-10-14 12:42:10 +0800178static constexpr uint8_t wdExpirationFlagReservedBit0 = 0x0;
179static constexpr uint8_t wdExpirationFlagReservedBit6 = 0x6;
180static constexpr uint8_t wdExpirationFlagReservedBit7 = 0x7;
William A. Kennington III52575252018-02-09 15:54:56 -0800181
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000182/**@brief The Set Watchdog Timer ipmi command.
183 *
184 * @param
185 * - timerUse
186 * - dontStopTimer
187 * - dontLog
188 * - timerAction
189 * - pretimeout
190 * - expireFlags
191 * - initialCountdown
192 *
193 * @return completion code on success.
194 **/
Yong Lia729bf42019-10-14 12:42:10 +0800195ipmi::RspType<>
196 ipmiSetWatchdogTimer(uint3_t timerUse, uint3_t reserved, bool dontStopTimer,
197 bool dontLog, uint3_t timeoutAction, uint1_t reserved1,
198 uint3_t preTimeoutInterrupt, uint1_t reserved2,
199 uint8_t preTimeoutInterval,
200 std::bitset<8> expFlagValue, uint16_t initialCountdown)
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800201{
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000202 if ((timerUse == wdTimerUseResTimer1) ||
203 (timerUse == wdTimerUseResTimer2) ||
204 (timerUse == wdTimerUseResTimer3) ||
Yong Li4dd71af2019-09-29 14:18:07 +0800205 (timeoutAction > wdTimeoutActionMax) ||
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000206 (preTimeoutInterrupt == wdTimeoutInterruptTimer) ||
Yong Lia729bf42019-10-14 12:42:10 +0800207 (reserved | reserved1 | reserved2 |
208 expFlagValue.test(wdExpirationFlagReservedBit0) |
209 expFlagValue.test(wdExpirationFlagReservedBit6) |
210 expFlagValue.test(wdExpirationFlagReservedBit7)))
William A. Kennington III52575252018-02-09 15:54:56 -0800211 {
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000212 return ipmi::responseInvalidFieldRequest();
William A. Kennington III52575252018-02-09 15:54:56 -0800213 }
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000214
215 if (preTimeoutInterval > (initialCountdown / 10))
216 {
217 return ipmi::responseInvalidFieldRequest();
218 }
219
Yong Li4dd71af2019-09-29 14:18:07 +0800220 timerNotLogFlags = dontLog;
221 timerPreTimeoutInterrupt = preTimeoutInterrupt;
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800222
William A. Kennington III52575252018-02-09 15:54:56 -0800223 try
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800224 {
William A. Kennington III52575252018-02-09 15:54:56 -0800225 WatchdogService wd_service;
226 // Stop the timer if the don't stop bit is not set
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000227 if (!(dontStopTimer))
William A. Kennington III52575252018-02-09 15:54:56 -0800228 {
229 wd_service.setEnabled(false);
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800230 }
231
William A. Kennington III52575252018-02-09 15:54:56 -0800232 // Set the action based on the request
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000233 const auto ipmi_action = static_cast<IpmiAction>(
234 static_cast<uint8_t>(timeoutAction) & wd_timeout_action_mask);
William A. Kennington IIIb638de22018-02-09 16:12:53 -0800235 wd_service.setExpireAction(ipmiActionToWdAction(ipmi_action));
William A. Kennington III52575252018-02-09 15:54:56 -0800236
William A. Kennington III7a0e5df2021-05-19 13:31:29 -0700237 const auto ipmiTimerUse = types::enum_cast<IpmiTimerUse>(timerUse);
Yong Li118907e2019-01-11 17:36:17 +0800238 wd_service.setTimerUse(ipmiTimerUseToWdTimerUse(ipmiTimerUse));
239
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000240 wd_service.setExpiredTimerUse(WatchdogService::TimerUse::Reserved);
241
Yong Lia729bf42019-10-14 12:42:10 +0800242 timerUseExpirationFlags &= ~expFlagValue;
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000243
William A. Kennington III52575252018-02-09 15:54:56 -0800244 // Set the new interval and the time remaining deci -> mill seconds
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000245 const uint64_t interval = initialCountdown * 100;
William A. Kennington III52575252018-02-09 15:54:56 -0800246 wd_service.setInterval(interval);
William A. Kennington IIIebc53cb2019-12-05 17:04:29 -0800247 wd_service.resetTimeRemaining(false);
William A. Kennington III52575252018-02-09 15:54:56 -0800248
William A. Kennington IIIde14a022018-02-09 16:11:18 -0800249 // Mark as initialized so that future resets behave correctly
250 wd_service.setInitialized(true);
251
William A. Kennington IIIbae471c2018-06-15 10:38:01 -0700252 lastCallSuccessful = true;
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000253 return ipmi::responseSuccess();
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800254 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700255 catch (const std::domain_error&)
William A. Kennington III52575252018-02-09 15:54:56 -0800256 {
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000257 return ipmi::responseInvalidFieldRequest();
William A. Kennington III52575252018-02-09 15:54:56 -0800258 }
William A. Kennington III021b4c12018-05-10 11:12:51 -0700259 catch (const InternalFailure& e)
260 {
William A. Kennington IIIbae471c2018-06-15 10:38:01 -0700261 reportError();
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000262 return ipmi::responseUnspecifiedError();
William A. Kennington III021b4c12018-05-10 11:12:51 -0700263 }
William A. Kennington III52575252018-02-09 15:54:56 -0800264 catch (const std::exception& e)
265 {
266 const std::string e_str = std::string("wd_set: ") + e.what();
267 log<level::ERR>(e_str.c_str());
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000268 return ipmi::responseUnspecifiedError();
William A. Kennington III52575252018-02-09 15:54:56 -0800269 }
270 catch (...)
271 {
272 log<level::ERR>("wd_set: Unknown Error");
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000273 return ipmi::responseUnspecifiedError();
William A. Kennington III52575252018-02-09 15:54:56 -0800274 }
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800275}
William A. Kennington III73f44512018-02-09 15:28:46 -0800276
277/** @brief Converts a DBUS Watchdog Action to IPMI defined action
278 * @param[in] wd_action The DBUS Watchdog Action
279 * @return The IpmiAction that the wd_action maps to
280 */
281IpmiAction wdActionToIpmiAction(WatchdogService::Action wd_action)
282{
Patrick Venture0b02be92018-08-31 11:55:55 -0700283 switch (wd_action)
William A. Kennington III73f44512018-02-09 15:28:46 -0800284 {
285 case WatchdogService::Action::None:
286 {
287 return IpmiAction::None;
288 }
289 case WatchdogService::Action::HardReset:
290 {
291 return IpmiAction::HardReset;
292 }
293 case WatchdogService::Action::PowerOff:
294 {
295 return IpmiAction::PowerOff;
296 }
297 case WatchdogService::Action::PowerCycle:
298 {
299 return IpmiAction::PowerCycle;
300 }
301 default:
302 {
303 // We have no method via IPMI to signal that the action is unknown
304 // or unmappable in some way.
305 // Just ignore the error and return NONE so the host can reconcile.
306 return IpmiAction::None;
307 }
308 }
309}
310
Yong Li118907e2019-01-11 17:36:17 +0800311IpmiTimerUse wdTimerUseToIpmiTimerUse(WatchdogService::TimerUse wdTimerUse)
312{
313 switch (wdTimerUse)
314 {
315 case WatchdogService::TimerUse::Reserved:
316 {
317 return IpmiTimerUse::Reserved;
318 }
319 case WatchdogService::TimerUse::BIOSFRB2:
320 {
321 return IpmiTimerUse::BIOSFRB2;
322 }
323 case WatchdogService::TimerUse::BIOSPOST:
324 {
325 return IpmiTimerUse::BIOSPOST;
326 }
327 case WatchdogService::TimerUse::OSLoad:
328 {
329 return IpmiTimerUse::OSLoad;
330 }
331
332 case WatchdogService::TimerUse::SMSOS:
333 {
334 return IpmiTimerUse::SMSOS;
335 }
336 case WatchdogService::TimerUse::OEM:
337 {
338 return IpmiTimerUse::OEM;
339 }
340 default:
341 {
342 return IpmiTimerUse::Reserved;
343 }
344 }
345}
346
William A. Kennington III73f44512018-02-09 15:28:46 -0800347static constexpr uint8_t wd_running = 0x1 << 6;
348
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000349/**@brief The getWatchdogTimer ipmi command.
350 *
351 * @return Completion code plus timer details.
352 * - timerUse
353 * - timerAction
354 * - pretimeout
355 * - expireFlags
356 * - initialCountdown
357 * - presentCountdown
358 **/
Yong Li4dd71af2019-09-29 14:18:07 +0800359ipmi::RspType<uint3_t, // timerUse - timer use
360 uint3_t, // timerUse - reserved
361 bool, // timerUse - timer is started
362 bool, // timerUse - don't log
363
364 uint3_t, // timerAction - timeout action
365 uint1_t, // timerAction - reserved
366 uint3_t, // timerAction - pre-timeout interrupt
367 uint1_t, // timerAction - reserved
368
Yong Lia729bf42019-10-14 12:42:10 +0800369 uint8_t, // pretimeout
370 std::bitset<8>, // expireFlags
371 uint16_t, // initial Countdown - Little Endian (deciseconds)
372 uint16_t // present Countdown - Little Endian (deciseconds)
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000373 >
374 ipmiGetWatchdogTimer()
William A. Kennington III73f44512018-02-09 15:28:46 -0800375{
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000376 uint16_t presentCountdown = 0;
377 uint8_t pretimeout = 0;
William A. Kennington III73f44512018-02-09 15:28:46 -0800378
379 try
380 {
381 WatchdogService wd_service;
382 WatchdogService::Properties wd_prop = wd_service.getProperties();
383
384 // Build and return the response
Yong Lif7c9db02019-01-15 13:45:33 +0800385 // Interval and timeRemaining need converted from milli -> deci seconds
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000386 uint16_t initialCountdown = htole16(wd_prop.interval / 100);
387
388 if (wd_prop.expiredTimerUse != WatchdogService::TimerUse::Reserved)
389 {
Yong Lia729bf42019-10-14 12:42:10 +0800390 timerUseExpirationFlags.set(static_cast<uint8_t>(
391 wdTimerUseToIpmiTimerUse(wd_prop.expiredTimerUse)));
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000392 }
393
William A. Kennington III73f44512018-02-09 15:28:46 -0800394 if (wd_prop.enabled)
395 {
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000396 presentCountdown = htole16(wd_prop.timeRemaining / 100);
Yong Lif7c9db02019-01-15 13:45:33 +0800397 }
398 else
399 {
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000400 if (wd_prop.expiredTimerUse == WatchdogService::TimerUse::Reserved)
401 {
402 presentCountdown = initialCountdown;
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000403 }
404 else
405 {
406 presentCountdown = 0;
Yong Li4dd71af2019-09-29 14:18:07 +0800407 // Automatically clear it whenever a timer expiration occurs.
408 timerNotLogFlags = false;
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000409 }
William A. Kennington III73f44512018-02-09 15:28:46 -0800410 }
Yong Li118907e2019-01-11 17:36:17 +0800411
William A. Kennington III73f44512018-02-09 15:28:46 -0800412 // TODO: Do something about having pretimeout support
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000413 pretimeout = 0;
414
William A. Kennington IIIbae471c2018-06-15 10:38:01 -0700415 lastCallSuccessful = true;
Yong Li4dd71af2019-09-29 14:18:07 +0800416 return ipmi::responseSuccess(
William A. Kennington III7a0e5df2021-05-19 13:31:29 -0700417 types::enum_cast<uint3_t>(
418 wdTimerUseToIpmiTimerUse(wd_prop.timerUse)),
419 0, wd_prop.enabled, timerNotLogFlags,
420 types::enum_cast<uint3_t>(
421 wdActionToIpmiAction(wd_prop.expireAction)),
422 0, timerPreTimeoutInterrupt, 0, pretimeout, timerUseExpirationFlags,
Yong Li4dd71af2019-09-29 14:18:07 +0800423 initialCountdown, presentCountdown);
William A. Kennington III73f44512018-02-09 15:28:46 -0800424 }
William A. Kennington III021b4c12018-05-10 11:12:51 -0700425 catch (const InternalFailure& e)
426 {
William A. Kennington IIIbae471c2018-06-15 10:38:01 -0700427 reportError();
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000428 return ipmi::responseUnspecifiedError();
William A. Kennington III021b4c12018-05-10 11:12:51 -0700429 }
William A. Kennington III73f44512018-02-09 15:28:46 -0800430 catch (const std::exception& e)
431 {
432 const std::string e_str = std::string("wd_get: ") + e.what();
433 log<level::ERR>(e_str.c_str());
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000434 return ipmi::responseUnspecifiedError();
William A. Kennington III73f44512018-02-09 15:28:46 -0800435 }
436 catch (...)
437 {
438 log<level::ERR>("wd_get: Unknown Error");
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000439 return ipmi::responseUnspecifiedError();
William A. Kennington III73f44512018-02-09 15:28:46 -0800440 }
441}