blob: b1eea1c2ff577841d8126842b98b91b790d6fe4d [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
Yong Li118907e2019-01-11 17:36:17 +080085static constexpr uint8_t wdTimerUseMask = 0x7;
86
Patrick Venture0b02be92018-08-31 11:55:55 -070087enum class IpmiAction : uint8_t
88{
William A. Kennington III52575252018-02-09 15:54:56 -080089 None = 0x0,
90 HardReset = 0x1,
91 PowerOff = 0x2,
92 PowerCycle = 0x3,
93};
94
William A. Kennington IIIb638de22018-02-09 16:12:53 -080095/** @brief Converts an IPMI Watchdog Action to DBUS defined action
96 * @param[in] ipmi_action The IPMI Watchdog Action
97 * @return The Watchdog Action that the ipmi_action maps to
98 */
99WatchdogService::Action ipmiActionToWdAction(IpmiAction ipmi_action)
100{
Patrick Venture0b02be92018-08-31 11:55:55 -0700101 switch (ipmi_action)
William A. Kennington IIIb638de22018-02-09 16:12:53 -0800102 {
103 case IpmiAction::None:
104 {
105 return WatchdogService::Action::None;
106 }
107 case IpmiAction::HardReset:
108 {
109 return WatchdogService::Action::HardReset;
110 }
111 case IpmiAction::PowerOff:
112 {
113 return WatchdogService::Action::PowerOff;
114 }
115 case IpmiAction::PowerCycle:
116 {
117 return WatchdogService::Action::PowerCycle;
118 }
119 default:
120 {
121 throw std::domain_error("IPMI Action is invalid");
122 }
123 }
124}
125
Yong Li118907e2019-01-11 17:36:17 +0800126enum class IpmiTimerUse : uint8_t
127{
128 Reserved = 0x0,
129 BIOSFRB2 = 0x1,
130 BIOSPOST = 0x2,
131 OSLoad = 0x3,
132 SMSOS = 0x4,
133 OEM = 0x5,
134};
135
136WatchdogService::TimerUse ipmiTimerUseToWdTimerUse(IpmiTimerUse ipmiTimerUse)
137{
138 switch (ipmiTimerUse)
139 {
140 case IpmiTimerUse::Reserved:
141 {
142 return WatchdogService::TimerUse::Reserved;
143 }
144 case IpmiTimerUse::BIOSFRB2:
145 {
146 return WatchdogService::TimerUse::BIOSFRB2;
147 }
148 case IpmiTimerUse::BIOSPOST:
149 {
150 return WatchdogService::TimerUse::BIOSPOST;
151 }
152 case IpmiTimerUse::OSLoad:
153 {
154 return WatchdogService::TimerUse::OSLoad;
155 }
156 case IpmiTimerUse::SMSOS:
157 {
158 return WatchdogService::TimerUse::SMSOS;
159 }
160 case IpmiTimerUse::OEM:
161 {
162 return WatchdogService::TimerUse::OEM;
163 }
164 default:
165 {
166 return WatchdogService::TimerUse::Reserved;
167 }
168 }
169}
170
Patrick Venture0b02be92018-08-31 11:55:55 -0700171struct wd_set_req
172{
William A. Kennington III52575252018-02-09 15:54:56 -0800173 uint8_t timer_use;
174 uint8_t timer_action;
Patrick Venture0b02be92018-08-31 11:55:55 -0700175 uint8_t pretimeout; // (seconds)
William A. Kennington III52575252018-02-09 15:54:56 -0800176 uint8_t expire_flags;
Patrick Venture0b02be92018-08-31 11:55:55 -0700177 uint16_t initial_countdown; // Little Endian (deciseconds)
178} __attribute__((packed));
William A. Kennington III52575252018-02-09 15:54:56 -0800179static_assert(sizeof(wd_set_req) == 6, "wd_set_req has invalid size.");
180static_assert(sizeof(wd_set_req) <= MAX_IPMI_BUFFER,
Patrick Venture0b02be92018-08-31 11:55:55 -0700181 "wd_get_res can't fit in request buffer.");
William A. Kennington III52575252018-02-09 15:54:56 -0800182
Patrick Venture0b02be92018-08-31 11:55:55 -0700183ipmi_ret_t ipmi_app_watchdog_set(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
184 ipmi_request_t request,
185 ipmi_response_t response,
186 ipmi_data_len_t data_len,
187 ipmi_context_t context)
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800188{
William A. Kennington III52575252018-02-09 15:54:56 -0800189 // Extract the request data
190 if (*data_len < sizeof(wd_set_req))
191 {
192 *data_len = 0;
193 return IPMI_CC_REQ_DATA_LEN_INVALID;
194 }
195 wd_set_req req;
196 memcpy(&req, request, sizeof(req));
197 req.initial_countdown = le16toh(req.initial_countdown);
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800198 *data_len = 0;
199
William A. Kennington III52575252018-02-09 15:54:56 -0800200 try
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800201 {
William A. Kennington III52575252018-02-09 15:54:56 -0800202 WatchdogService wd_service;
203 // Stop the timer if the don't stop bit is not set
204 if (!(req.timer_use & wd_dont_stop))
205 {
206 wd_service.setEnabled(false);
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800207 }
208
William A. Kennington III52575252018-02-09 15:54:56 -0800209 // Set the action based on the request
Patrick Venture0b02be92018-08-31 11:55:55 -0700210 const auto ipmi_action =
211 static_cast<IpmiAction>(req.timer_action & wd_timeout_action_mask);
William A. Kennington IIIb638de22018-02-09 16:12:53 -0800212 wd_service.setExpireAction(ipmiActionToWdAction(ipmi_action));
William A. Kennington III52575252018-02-09 15:54:56 -0800213
Yong Li118907e2019-01-11 17:36:17 +0800214 const auto ipmiTimerUse =
215 static_cast<IpmiTimerUse>(req.timer_use & wdTimerUseMask);
216 wd_service.setTimerUse(ipmiTimerUseToWdTimerUse(ipmiTimerUse));
217
William A. Kennington III52575252018-02-09 15:54:56 -0800218 // Set the new interval and the time remaining deci -> mill seconds
219 const uint64_t interval = req.initial_countdown * 100;
220 wd_service.setInterval(interval);
221 wd_service.setTimeRemaining(interval);
222
William A. Kennington IIIde14a022018-02-09 16:11:18 -0800223 // Mark as initialized so that future resets behave correctly
224 wd_service.setInitialized(true);
225
William A. Kennington IIIbae471c2018-06-15 10:38:01 -0700226 lastCallSuccessful = true;
William A. Kennington III52575252018-02-09 15:54:56 -0800227 return IPMI_CC_OK;
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800228 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700229 catch (const std::domain_error&)
William A. Kennington III52575252018-02-09 15:54:56 -0800230 {
231 return IPMI_CC_INVALID_FIELD_REQUEST;
232 }
William A. Kennington III021b4c12018-05-10 11:12:51 -0700233 catch (const InternalFailure& e)
234 {
William A. Kennington IIIbae471c2018-06-15 10:38:01 -0700235 reportError();
William A. Kennington III021b4c12018-05-10 11:12:51 -0700236 return IPMI_CC_UNSPECIFIED_ERROR;
237 }
William A. Kennington III52575252018-02-09 15:54:56 -0800238 catch (const std::exception& e)
239 {
240 const std::string e_str = std::string("wd_set: ") + e.what();
241 log<level::ERR>(e_str.c_str());
William A. Kennington IIIbae471c2018-06-15 10:38:01 -0700242 reportError();
William A. Kennington III52575252018-02-09 15:54:56 -0800243 return IPMI_CC_UNSPECIFIED_ERROR;
244 }
245 catch (...)
246 {
247 log<level::ERR>("wd_set: Unknown Error");
William A. Kennington IIIbae471c2018-06-15 10:38:01 -0700248 reportError();
William A. Kennington III52575252018-02-09 15:54:56 -0800249 return IPMI_CC_UNSPECIFIED_ERROR;
250 }
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800251}
William A. Kennington III73f44512018-02-09 15:28:46 -0800252
253/** @brief Converts a DBUS Watchdog Action to IPMI defined action
254 * @param[in] wd_action The DBUS Watchdog Action
255 * @return The IpmiAction that the wd_action maps to
256 */
257IpmiAction wdActionToIpmiAction(WatchdogService::Action wd_action)
258{
Patrick Venture0b02be92018-08-31 11:55:55 -0700259 switch (wd_action)
William A. Kennington III73f44512018-02-09 15:28:46 -0800260 {
261 case WatchdogService::Action::None:
262 {
263 return IpmiAction::None;
264 }
265 case WatchdogService::Action::HardReset:
266 {
267 return IpmiAction::HardReset;
268 }
269 case WatchdogService::Action::PowerOff:
270 {
271 return IpmiAction::PowerOff;
272 }
273 case WatchdogService::Action::PowerCycle:
274 {
275 return IpmiAction::PowerCycle;
276 }
277 default:
278 {
279 // We have no method via IPMI to signal that the action is unknown
280 // or unmappable in some way.
281 // Just ignore the error and return NONE so the host can reconcile.
282 return IpmiAction::None;
283 }
284 }
285}
286
Yong Li118907e2019-01-11 17:36:17 +0800287IpmiTimerUse wdTimerUseToIpmiTimerUse(WatchdogService::TimerUse wdTimerUse)
288{
289 switch (wdTimerUse)
290 {
291 case WatchdogService::TimerUse::Reserved:
292 {
293 return IpmiTimerUse::Reserved;
294 }
295 case WatchdogService::TimerUse::BIOSFRB2:
296 {
297 return IpmiTimerUse::BIOSFRB2;
298 }
299 case WatchdogService::TimerUse::BIOSPOST:
300 {
301 return IpmiTimerUse::BIOSPOST;
302 }
303 case WatchdogService::TimerUse::OSLoad:
304 {
305 return IpmiTimerUse::OSLoad;
306 }
307
308 case WatchdogService::TimerUse::SMSOS:
309 {
310 return IpmiTimerUse::SMSOS;
311 }
312 case WatchdogService::TimerUse::OEM:
313 {
314 return IpmiTimerUse::OEM;
315 }
316 default:
317 {
318 return IpmiTimerUse::Reserved;
319 }
320 }
321}
322
Patrick Venture0b02be92018-08-31 11:55:55 -0700323struct wd_get_res
324{
William A. Kennington III73f44512018-02-09 15:28:46 -0800325 uint8_t timer_use;
326 uint8_t timer_action;
327 uint8_t pretimeout;
328 uint8_t expire_flags;
Patrick Venture0b02be92018-08-31 11:55:55 -0700329 uint16_t initial_countdown; // Little Endian (deciseconds)
330 uint16_t present_countdown; // Little Endian (deciseconds)
331} __attribute__((packed));
William A. Kennington III73f44512018-02-09 15:28:46 -0800332static_assert(sizeof(wd_get_res) == 8, "wd_get_res has invalid size.");
333static_assert(sizeof(wd_get_res) <= MAX_IPMI_BUFFER,
Patrick Venture0b02be92018-08-31 11:55:55 -0700334 "wd_get_res can't fit in response buffer.");
William A. Kennington III73f44512018-02-09 15:28:46 -0800335
336static constexpr uint8_t wd_dont_log = 0x1 << 7;
337static constexpr uint8_t wd_running = 0x1 << 6;
338
Patrick Venture0b02be92018-08-31 11:55:55 -0700339ipmi_ret_t ipmi_app_watchdog_get(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
340 ipmi_request_t request,
341 ipmi_response_t response,
342 ipmi_data_len_t data_len,
343 ipmi_context_t context)
William A. Kennington III73f44512018-02-09 15:28:46 -0800344{
345 // Assume we will fail and send no data outside the return code
346 *data_len = 0;
347
348 try
349 {
350 WatchdogService wd_service;
351 WatchdogService::Properties wd_prop = wd_service.getProperties();
352
353 // Build and return the response
354 wd_get_res res;
355 res.timer_use = wd_dont_log;
Patrick Venture0b02be92018-08-31 11:55:55 -0700356 res.timer_action =
357 static_cast<uint8_t>(wdActionToIpmiAction(wd_prop.expireAction));
Yong Lif7c9db02019-01-15 13:45:33 +0800358
359 // Interval and timeRemaining need converted from milli -> deci seconds
360 res.initial_countdown = htole16(wd_prop.interval / 100);
William A. Kennington III73f44512018-02-09 15:28:46 -0800361 if (wd_prop.enabled)
362 {
363 res.timer_use |= wd_running;
Yong Lif7c9db02019-01-15 13:45:33 +0800364 res.present_countdown = htole16(wd_prop.timeRemaining / 100);
365 }
366 else
367 {
368 res.present_countdown = res.initial_countdown;
William A. Kennington III73f44512018-02-09 15:28:46 -0800369 }
Yong Li118907e2019-01-11 17:36:17 +0800370
371 res.timer_use |=
372 static_cast<uint8_t>(wdTimerUseToIpmiTimerUse(wd_prop.timerUse));
373
William A. Kennington III73f44512018-02-09 15:28:46 -0800374 // TODO: Do something about having pretimeout support
375 res.pretimeout = 0;
376 res.expire_flags = 0;
William A. Kennington III73f44512018-02-09 15:28:46 -0800377 memcpy(response, &res, sizeof(res));
378 *data_len = sizeof(res);
William A. Kennington IIIbae471c2018-06-15 10:38:01 -0700379 lastCallSuccessful = true;
William A. Kennington III73f44512018-02-09 15:28:46 -0800380 return IPMI_CC_OK;
381 }
William A. Kennington III021b4c12018-05-10 11:12:51 -0700382 catch (const InternalFailure& e)
383 {
William A. Kennington IIIbae471c2018-06-15 10:38:01 -0700384 reportError();
William A. Kennington III021b4c12018-05-10 11:12:51 -0700385 return IPMI_CC_UNSPECIFIED_ERROR;
386 }
William A. Kennington III73f44512018-02-09 15:28:46 -0800387 catch (const std::exception& e)
388 {
389 const std::string e_str = std::string("wd_get: ") + e.what();
390 log<level::ERR>(e_str.c_str());
William A. Kennington IIIbae471c2018-06-15 10:38:01 -0700391 reportError();
William A. Kennington III73f44512018-02-09 15:28:46 -0800392 return IPMI_CC_UNSPECIFIED_ERROR;
393 }
394 catch (...)
395 {
396 log<level::ERR>("wd_get: Unknown Error");
William A. Kennington IIIbae471c2018-06-15 10:38:01 -0700397 reportError();
William A. Kennington III73f44512018-02-09 15:28:46 -0800398 return IPMI_CC_UNSPECIFIED_ERROR;
399 }
400}