blob: ff07b024ac482d40491ad34a7276cf7e473096a7 [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
7#include <cstdint>
Vernon Mauerye08fbff2019-04-03 09:19:34 -07008#include <ipmid/api.hpp>
William A. Kennington III021b4c12018-05-10 11:12:51 -07009#include <phosphor-logging/elog-errors.hpp>
Patrick Venture0b02be92018-08-31 11:55:55 -070010#include <phosphor-logging/elog.hpp>
William A. Kennington III52575252018-02-09 15:54:56 -080011#include <phosphor-logging/log.hpp>
12#include <string>
William A. Kennington III021b4c12018-05-10 11:12:51 -070013#include <xyz/openbmc_project/Common/error.hpp>
Patrick Venture894571d2017-11-09 14:46:54 -080014
William A. Kennington IIIbae471c2018-06-15 10:38:01 -070015using phosphor::logging::commit;
William A. Kennington III52575252018-02-09 15:54:56 -080016using phosphor::logging::level;
17using phosphor::logging::log;
William A. Kennington III021b4c12018-05-10 11:12:51 -070018using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
Patrick Venture5e6ac712017-10-25 12:16:19 -070019
William A. Kennington IIIbae471c2018-06-15 10:38:01 -070020static bool lastCallSuccessful = false;
21
22void reportError()
23{
24 // We don't want to fill the SEL with errors if the daemon dies and doesn't
25 // come back but the watchdog keeps on ticking. Instead, we only report the
26 // error if we haven't reported one since the last successful call
27 if (!lastCallSuccessful)
28 {
29 return;
30 }
31 lastCallSuccessful = false;
32
33 // TODO: This slow down the end of the IPMI transaction waiting
34 // for the commit to finish. commit<>() can take at least 5 seconds
35 // to complete. 5s is very slow for an IPMI command and ends up
36 // congesting the IPMI channel needlessly, especially if the watchdog
37 // is ticking fairly quickly and we have some transient issues.
38 commit<InternalFailure>();
39}
40
Vernon Mauery11df4f62019-03-25 14:17:54 -070041ipmi::RspType<> ipmiAppResetWatchdogTimer()
Patrick Venture5e6ac712017-10-25 12:16:19 -070042{
William A. Kennington III52575252018-02-09 15:54:56 -080043 try
44 {
45 WatchdogService wd_service;
William A. Kennington III52575252018-02-09 15:54:56 -080046
William A. Kennington IIIde14a022018-02-09 16:11:18 -080047 // Notify the caller if we haven't initialized our timer yet
48 // so it can configure actions and timeouts
William A. Kennington III2ecf5122018-04-27 14:31:51 -070049 if (!wd_service.getInitialized())
William A. Kennington IIIde14a022018-02-09 16:11:18 -080050 {
William A. Kennington IIIbae471c2018-06-15 10:38:01 -070051 lastCallSuccessful = true;
Vernon Mauery11df4f62019-03-25 14:17:54 -070052
53 constexpr uint8_t ccWatchdogNotInit = 0x80;
54 return ipmi::response(ccWatchdogNotInit);
William A. Kennington IIIde14a022018-02-09 16:11:18 -080055 }
56
William A. Kennington III4b017a92018-04-27 14:31:08 -070057 // The ipmi standard dictates we enable the watchdog during reset
58 wd_service.resetTimeRemaining(true);
William A. Kennington IIIbae471c2018-06-15 10:38:01 -070059 lastCallSuccessful = true;
Vernon Mauery11df4f62019-03-25 14:17:54 -070060 return ipmi::responseSuccess();
Patrick Venture5e6ac712017-10-25 12:16:19 -070061 }
William A. Kennington III021b4c12018-05-10 11:12:51 -070062 catch (const InternalFailure& e)
63 {
William A. Kennington IIIbae471c2018-06-15 10:38:01 -070064 reportError();
Vernon Mauery11df4f62019-03-25 14:17:54 -070065 return ipmi::responseUnspecifiedError();
William A. Kennington III021b4c12018-05-10 11:12:51 -070066 }
William A. Kennington III52575252018-02-09 15:54:56 -080067 catch (const std::exception& e)
68 {
69 const std::string e_str = std::string("wd_reset: ") + e.what();
70 log<level::ERR>(e_str.c_str());
William A. Kennington IIIbae471c2018-06-15 10:38:01 -070071 reportError();
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");
William A. Kennington IIIbae471c2018-06-15 10:38:01 -070077 reportError();
Vernon Mauery11df4f62019-03-25 14:17:54 -070078 return ipmi::responseUnspecifiedError();
William A. Kennington III5325f2c2018-01-08 15:17:09 -080079 }
Patrick Venture5e6ac712017-10-25 12:16:19 -070080}
William A. Kennington III61d5f7b2018-02-09 15:23:53 -080081
William A. Kennington III52575252018-02-09 15:54:56 -080082static constexpr uint8_t wd_dont_stop = 0x1 << 6;
83static constexpr uint8_t wd_timeout_action_mask = 0x3;
84
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +000085static constexpr uint8_t wdTimerUseResTimer1 = 0x0;
86static constexpr uint8_t wdTimerUseResTimer2 = 0x6;
87static constexpr uint8_t wdTimerUseResTimer3 = 0x7;
88
89static constexpr uint8_t wdTimeoutActionTimer = 0x40;
90static constexpr uint8_t wdTimeoutInterruptTimer = 0x04;
Yong Li118907e2019-01-11 17:36:17 +080091
Patrick Venture0b02be92018-08-31 11:55:55 -070092enum class IpmiAction : uint8_t
93{
William A. Kennington III52575252018-02-09 15:54:56 -080094 None = 0x0,
95 HardReset = 0x1,
96 PowerOff = 0x2,
97 PowerCycle = 0x3,
98};
99
William A. Kennington IIIb638de22018-02-09 16:12:53 -0800100/** @brief Converts an IPMI Watchdog Action to DBUS defined action
101 * @param[in] ipmi_action The IPMI Watchdog Action
102 * @return The Watchdog Action that the ipmi_action maps to
103 */
104WatchdogService::Action ipmiActionToWdAction(IpmiAction ipmi_action)
105{
Patrick Venture0b02be92018-08-31 11:55:55 -0700106 switch (ipmi_action)
William A. Kennington IIIb638de22018-02-09 16:12:53 -0800107 {
108 case IpmiAction::None:
109 {
110 return WatchdogService::Action::None;
111 }
112 case IpmiAction::HardReset:
113 {
114 return WatchdogService::Action::HardReset;
115 }
116 case IpmiAction::PowerOff:
117 {
118 return WatchdogService::Action::PowerOff;
119 }
120 case IpmiAction::PowerCycle:
121 {
122 return WatchdogService::Action::PowerCycle;
123 }
124 default:
125 {
126 throw std::domain_error("IPMI Action is invalid");
127 }
128 }
129}
130
Yong Li118907e2019-01-11 17:36:17 +0800131enum class IpmiTimerUse : uint8_t
132{
133 Reserved = 0x0,
134 BIOSFRB2 = 0x1,
135 BIOSPOST = 0x2,
136 OSLoad = 0x3,
137 SMSOS = 0x4,
138 OEM = 0x5,
139};
140
141WatchdogService::TimerUse ipmiTimerUseToWdTimerUse(IpmiTimerUse ipmiTimerUse)
142{
143 switch (ipmiTimerUse)
144 {
145 case IpmiTimerUse::Reserved:
146 {
147 return WatchdogService::TimerUse::Reserved;
148 }
149 case IpmiTimerUse::BIOSFRB2:
150 {
151 return WatchdogService::TimerUse::BIOSFRB2;
152 }
153 case IpmiTimerUse::BIOSPOST:
154 {
155 return WatchdogService::TimerUse::BIOSPOST;
156 }
157 case IpmiTimerUse::OSLoad:
158 {
159 return WatchdogService::TimerUse::OSLoad;
160 }
161 case IpmiTimerUse::SMSOS:
162 {
163 return WatchdogService::TimerUse::SMSOS;
164 }
165 case IpmiTimerUse::OEM:
166 {
167 return WatchdogService::TimerUse::OEM;
168 }
169 default:
170 {
171 return WatchdogService::TimerUse::Reserved;
172 }
173 }
174}
175
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000176static uint8_t timerLogFlags = 0;
177static uint8_t timerActions = 0;
178static uint8_t timerUseExpirationFlags = 0;
William A. Kennington III52575252018-02-09 15:54:56 -0800179
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000180/**@brief The Set Watchdog Timer ipmi command.
181 *
182 * @param
183 * - timerUse
184 * - dontStopTimer
185 * - dontLog
186 * - timerAction
187 * - pretimeout
188 * - expireFlags
189 * - initialCountdown
190 *
191 * @return completion code on success.
192 **/
193ipmi::RspType<> ipmiSetWatchdogTimer(
194 uint3_t timerUse, uint3_t reserved, bool dontStopTimer, bool dontLog,
195 uint3_t timeoutAction, uint1_t reserved1, uint3_t preTimeoutInterrupt,
196 uint1_t reserved2, uint8_t preTimeoutInterval, uint1_t reserved3,
197 uint5_t expFlagValue, uint2_t reserved4, uint16_t initialCountdown)
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800198{
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000199 if ((timerUse == wdTimerUseResTimer1) ||
200 (timerUse == wdTimerUseResTimer2) ||
201 (timerUse == wdTimerUseResTimer3) ||
202 (timeoutAction == wdTimeoutActionTimer) ||
203 (preTimeoutInterrupt == wdTimeoutInterruptTimer) ||
204 (reserved1 | reserved2 | reserved3 | reserved4))
William A. Kennington III52575252018-02-09 15:54:56 -0800205 {
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000206 return ipmi::responseInvalidFieldRequest();
William A. Kennington III52575252018-02-09 15:54:56 -0800207 }
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000208
209 if (preTimeoutInterval > (initialCountdown / 10))
210 {
211 return ipmi::responseInvalidFieldRequest();
212 }
213
214 timerLogFlags = static_cast<uint8_t>(dontLog);
215 timerActions &= static_cast<uint8_t>(timeoutAction) |
216 static_cast<uint8_t>(preTimeoutInterrupt) << 4;
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800217
William A. Kennington III52575252018-02-09 15:54:56 -0800218 try
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800219 {
William A. Kennington III52575252018-02-09 15:54:56 -0800220 WatchdogService wd_service;
221 // Stop the timer if the don't stop bit is not set
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000222 if (!(dontStopTimer))
William A. Kennington III52575252018-02-09 15:54:56 -0800223 {
224 wd_service.setEnabled(false);
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800225 }
226
William A. Kennington III52575252018-02-09 15:54:56 -0800227 // Set the action based on the request
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000228 const auto ipmi_action = static_cast<IpmiAction>(
229 static_cast<uint8_t>(timeoutAction) & wd_timeout_action_mask);
William A. Kennington IIIb638de22018-02-09 16:12:53 -0800230 wd_service.setExpireAction(ipmiActionToWdAction(ipmi_action));
William A. Kennington III52575252018-02-09 15:54:56 -0800231
Yong Li118907e2019-01-11 17:36:17 +0800232 const auto ipmiTimerUse =
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000233 static_cast<IpmiTimerUse>(static_cast<uint8_t>(timerUse));
Yong Li118907e2019-01-11 17:36:17 +0800234 wd_service.setTimerUse(ipmiTimerUseToWdTimerUse(ipmiTimerUse));
235
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000236 wd_service.setExpiredTimerUse(WatchdogService::TimerUse::Reserved);
237
238 timerUseExpirationFlags &= static_cast<uint8_t>(~expFlagValue) << 2;
239
William A. Kennington III52575252018-02-09 15:54:56 -0800240 // Set the new interval and the time remaining deci -> mill seconds
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000241 const uint64_t interval = initialCountdown * 100;
William A. Kennington III52575252018-02-09 15:54:56 -0800242 wd_service.setInterval(interval);
243 wd_service.setTimeRemaining(interval);
244
William A. Kennington IIIde14a022018-02-09 16:11:18 -0800245 // Mark as initialized so that future resets behave correctly
246 wd_service.setInitialized(true);
247
William A. Kennington IIIbae471c2018-06-15 10:38:01 -0700248 lastCallSuccessful = true;
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000249 return ipmi::responseSuccess();
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800250 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700251 catch (const std::domain_error&)
William A. Kennington III52575252018-02-09 15:54:56 -0800252 {
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000253 return ipmi::responseInvalidFieldRequest();
William A. Kennington III52575252018-02-09 15:54:56 -0800254 }
William A. Kennington III021b4c12018-05-10 11:12:51 -0700255 catch (const InternalFailure& e)
256 {
William A. Kennington IIIbae471c2018-06-15 10:38:01 -0700257 reportError();
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000258 return ipmi::responseUnspecifiedError();
William A. Kennington III021b4c12018-05-10 11:12:51 -0700259 }
William A. Kennington III52575252018-02-09 15:54:56 -0800260 catch (const std::exception& e)
261 {
262 const std::string e_str = std::string("wd_set: ") + e.what();
263 log<level::ERR>(e_str.c_str());
William A. Kennington IIIbae471c2018-06-15 10:38:01 -0700264 reportError();
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000265 return ipmi::responseUnspecifiedError();
William A. Kennington III52575252018-02-09 15:54:56 -0800266 }
267 catch (...)
268 {
269 log<level::ERR>("wd_set: Unknown Error");
William A. Kennington IIIbae471c2018-06-15 10:38:01 -0700270 reportError();
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000271 return ipmi::responseUnspecifiedError();
William A. Kennington III52575252018-02-09 15:54:56 -0800272 }
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800273}
William A. Kennington III73f44512018-02-09 15:28:46 -0800274
275/** @brief Converts a DBUS Watchdog Action to IPMI defined action
276 * @param[in] wd_action The DBUS Watchdog Action
277 * @return The IpmiAction that the wd_action maps to
278 */
279IpmiAction wdActionToIpmiAction(WatchdogService::Action wd_action)
280{
Patrick Venture0b02be92018-08-31 11:55:55 -0700281 switch (wd_action)
William A. Kennington III73f44512018-02-09 15:28:46 -0800282 {
283 case WatchdogService::Action::None:
284 {
285 return IpmiAction::None;
286 }
287 case WatchdogService::Action::HardReset:
288 {
289 return IpmiAction::HardReset;
290 }
291 case WatchdogService::Action::PowerOff:
292 {
293 return IpmiAction::PowerOff;
294 }
295 case WatchdogService::Action::PowerCycle:
296 {
297 return IpmiAction::PowerCycle;
298 }
299 default:
300 {
301 // We have no method via IPMI to signal that the action is unknown
302 // or unmappable in some way.
303 // Just ignore the error and return NONE so the host can reconcile.
304 return IpmiAction::None;
305 }
306 }
307}
308
Yong Li118907e2019-01-11 17:36:17 +0800309IpmiTimerUse wdTimerUseToIpmiTimerUse(WatchdogService::TimerUse wdTimerUse)
310{
311 switch (wdTimerUse)
312 {
313 case WatchdogService::TimerUse::Reserved:
314 {
315 return IpmiTimerUse::Reserved;
316 }
317 case WatchdogService::TimerUse::BIOSFRB2:
318 {
319 return IpmiTimerUse::BIOSFRB2;
320 }
321 case WatchdogService::TimerUse::BIOSPOST:
322 {
323 return IpmiTimerUse::BIOSPOST;
324 }
325 case WatchdogService::TimerUse::OSLoad:
326 {
327 return IpmiTimerUse::OSLoad;
328 }
329
330 case WatchdogService::TimerUse::SMSOS:
331 {
332 return IpmiTimerUse::SMSOS;
333 }
334 case WatchdogService::TimerUse::OEM:
335 {
336 return IpmiTimerUse::OEM;
337 }
338 default:
339 {
340 return IpmiTimerUse::Reserved;
341 }
342 }
343}
344
William A. Kennington III73f44512018-02-09 15:28:46 -0800345static constexpr uint8_t wd_running = 0x1 << 6;
346
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000347/**@brief The getWatchdogTimer ipmi command.
348 *
349 * @return Completion code plus timer details.
350 * - timerUse
351 * - timerAction
352 * - pretimeout
353 * - expireFlags
354 * - initialCountdown
355 * - presentCountdown
356 **/
357ipmi::RspType<uint8_t, // timerUse
358 uint8_t, // timerAction
359 uint8_t, // pretimeout
360 uint8_t, // expireFlags
361 uint16_t, // initial Countdown - Little Endian (deciseconds)
362 uint16_t // present Countdown - Little Endian (deciseconds)
363 >
364 ipmiGetWatchdogTimer()
William A. Kennington III73f44512018-02-09 15:28:46 -0800365{
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000366 uint8_t expireFlags = 0;
367 uint16_t presentCountdown = 0;
368 uint8_t pretimeout = 0;
William A. Kennington III73f44512018-02-09 15:28:46 -0800369
370 try
371 {
372 WatchdogService wd_service;
373 WatchdogService::Properties wd_prop = wd_service.getProperties();
374
375 // Build and return the response
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000376 uint8_t timerUse = 0;
377 timerUse |= timerLogFlags;
378
379 uint8_t timerAction = timerActions;
Yong Lif7c9db02019-01-15 13:45:33 +0800380
381 // Interval and timeRemaining need converted from milli -> deci seconds
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000382 uint16_t initialCountdown = htole16(wd_prop.interval / 100);
383
384 if (wd_prop.expiredTimerUse != WatchdogService::TimerUse::Reserved)
385 {
386 timerUseExpirationFlags |=
387 1 << static_cast<uint8_t>(
388 wdTimerUseToIpmiTimerUse(wd_prop.expiredTimerUse));
389 }
390
William A. Kennington III73f44512018-02-09 15:28:46 -0800391 if (wd_prop.enabled)
392 {
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000393 timerUse |= wd_running;
394 presentCountdown = htole16(wd_prop.timeRemaining / 100);
395 expireFlags = 0;
Yong Lif7c9db02019-01-15 13:45:33 +0800396 }
397 else
398 {
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000399 if (wd_prop.expiredTimerUse == WatchdogService::TimerUse::Reserved)
400 {
401 presentCountdown = initialCountdown;
402 expireFlags = 0;
403 }
404 else
405 {
406 presentCountdown = 0;
407 expireFlags = timerUseExpirationFlags;
408 }
William A. Kennington III73f44512018-02-09 15:28:46 -0800409 }
Yong Li118907e2019-01-11 17:36:17 +0800410
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000411 timerUse |=
Yong Li118907e2019-01-11 17:36:17 +0800412 static_cast<uint8_t>(wdTimerUseToIpmiTimerUse(wd_prop.timerUse));
413
William A. Kennington III73f44512018-02-09 15:28:46 -0800414 // TODO: Do something about having pretimeout support
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000415 pretimeout = 0;
416
William A. Kennington IIIbae471c2018-06-15 10:38:01 -0700417 lastCallSuccessful = true;
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000418 return ipmi::responseSuccess(timerUse, timerAction, pretimeout,
419 expireFlags, initialCountdown,
420 presentCountdown);
William A. Kennington III73f44512018-02-09 15:28:46 -0800421 }
William A. Kennington III021b4c12018-05-10 11:12:51 -0700422 catch (const InternalFailure& e)
423 {
William A. Kennington IIIbae471c2018-06-15 10:38:01 -0700424 reportError();
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000425 return ipmi::responseUnspecifiedError();
William A. Kennington III021b4c12018-05-10 11:12:51 -0700426 }
William A. Kennington III73f44512018-02-09 15:28:46 -0800427 catch (const std::exception& e)
428 {
429 const std::string e_str = std::string("wd_get: ") + e.what();
430 log<level::ERR>(e_str.c_str());
William A. Kennington IIIbae471c2018-06-15 10:38:01 -0700431 reportError();
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000432 return ipmi::responseUnspecifiedError();
William A. Kennington III73f44512018-02-09 15:28:46 -0800433 }
434 catch (...)
435 {
436 log<level::ERR>("wd_get: Unknown Error");
William A. Kennington IIIbae471c2018-06-15 10:38:01 -0700437 reportError();
Deepak Kumar Sahucfae9482019-05-20 14:58:58 +0000438 return ipmi::responseUnspecifiedError();
William A. Kennington III73f44512018-02-09 15:28:46 -0800439 }
440}