blob: 51ba395dfe84a7ae48ac818a08f3f7c9871b5a80 [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
Patrick Venture0b02be92018-08-31 11:55:55 -070041ipmi_ret_t ipmi_app_watchdog_reset(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
42 ipmi_request_t request,
43 ipmi_response_t response,
44 ipmi_data_len_t data_len,
45 ipmi_context_t context)
Patrick Venture5e6ac712017-10-25 12:16:19 -070046{
William A. Kennington III52575252018-02-09 15:54:56 -080047 // We never return data with this command so immediately get rid of it
Patrick Venture5e6ac712017-10-25 12:16:19 -070048 *data_len = 0;
49
William A. Kennington III52575252018-02-09 15:54:56 -080050 try
51 {
52 WatchdogService wd_service;
William A. Kennington III52575252018-02-09 15:54:56 -080053
William A. Kennington IIIde14a022018-02-09 16:11:18 -080054 // Notify the caller if we haven't initialized our timer yet
55 // so it can configure actions and timeouts
William A. Kennington III2ecf5122018-04-27 14:31:51 -070056 if (!wd_service.getInitialized())
William A. Kennington IIIde14a022018-02-09 16:11:18 -080057 {
William A. Kennington IIIbae471c2018-06-15 10:38:01 -070058 lastCallSuccessful = true;
William A. Kennington IIIde14a022018-02-09 16:11:18 -080059 return IPMI_WDOG_CC_NOT_INIT;
60 }
61
William A. Kennington III4b017a92018-04-27 14:31:08 -070062 // The ipmi standard dictates we enable the watchdog during reset
63 wd_service.resetTimeRemaining(true);
William A. Kennington IIIbae471c2018-06-15 10:38:01 -070064 lastCallSuccessful = true;
William A. Kennington III52575252018-02-09 15:54:56 -080065 return IPMI_CC_OK;
Patrick Venture5e6ac712017-10-25 12:16:19 -070066 }
William A. Kennington III021b4c12018-05-10 11:12:51 -070067 catch (const InternalFailure& e)
68 {
William A. Kennington IIIbae471c2018-06-15 10:38:01 -070069 reportError();
William A. Kennington III021b4c12018-05-10 11:12:51 -070070 return IPMI_CC_UNSPECIFIED_ERROR;
71 }
William A. Kennington III52575252018-02-09 15:54:56 -080072 catch (const std::exception& e)
73 {
74 const std::string e_str = std::string("wd_reset: ") + e.what();
75 log<level::ERR>(e_str.c_str());
William A. Kennington IIIbae471c2018-06-15 10:38:01 -070076 reportError();
William A. Kennington III52575252018-02-09 15:54:56 -080077 return IPMI_CC_UNSPECIFIED_ERROR;
William A. Kennington III5325f2c2018-01-08 15:17:09 -080078 }
William A. Kennington III52575252018-02-09 15:54:56 -080079 catch (...)
80 {
81 log<level::ERR>("wd_reset: Unknown Error");
William A. Kennington IIIbae471c2018-06-15 10:38:01 -070082 reportError();
William A. Kennington III52575252018-02-09 15:54:56 -080083 return IPMI_CC_UNSPECIFIED_ERROR;
William A. Kennington III5325f2c2018-01-08 15:17:09 -080084 }
Patrick Venture5e6ac712017-10-25 12:16:19 -070085}
William A. Kennington III61d5f7b2018-02-09 15:23:53 -080086
William A. Kennington III52575252018-02-09 15:54:56 -080087static constexpr uint8_t wd_dont_stop = 0x1 << 6;
88static constexpr uint8_t wd_timeout_action_mask = 0x3;
89
Yong Li118907e2019-01-11 17:36:17 +080090static constexpr uint8_t wdTimerUseMask = 0x7;
91
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
Patrick Venture0b02be92018-08-31 11:55:55 -0700176struct wd_set_req
177{
William A. Kennington III52575252018-02-09 15:54:56 -0800178 uint8_t timer_use;
179 uint8_t timer_action;
Patrick Venture0b02be92018-08-31 11:55:55 -0700180 uint8_t pretimeout; // (seconds)
William A. Kennington III52575252018-02-09 15:54:56 -0800181 uint8_t expire_flags;
Patrick Venture0b02be92018-08-31 11:55:55 -0700182 uint16_t initial_countdown; // Little Endian (deciseconds)
183} __attribute__((packed));
William A. Kennington III52575252018-02-09 15:54:56 -0800184static_assert(sizeof(wd_set_req) == 6, "wd_set_req has invalid size.");
185static_assert(sizeof(wd_set_req) <= MAX_IPMI_BUFFER,
Patrick Venture0b02be92018-08-31 11:55:55 -0700186 "wd_get_res can't fit in request buffer.");
William A. Kennington III52575252018-02-09 15:54:56 -0800187
Patrick Venture0b02be92018-08-31 11:55:55 -0700188ipmi_ret_t ipmi_app_watchdog_set(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
189 ipmi_request_t request,
190 ipmi_response_t response,
191 ipmi_data_len_t data_len,
192 ipmi_context_t context)
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800193{
William A. Kennington III52575252018-02-09 15:54:56 -0800194 // Extract the request data
195 if (*data_len < sizeof(wd_set_req))
196 {
197 *data_len = 0;
198 return IPMI_CC_REQ_DATA_LEN_INVALID;
199 }
200 wd_set_req req;
201 memcpy(&req, request, sizeof(req));
202 req.initial_countdown = le16toh(req.initial_countdown);
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800203 *data_len = 0;
204
William A. Kennington III52575252018-02-09 15:54:56 -0800205 try
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800206 {
William A. Kennington III52575252018-02-09 15:54:56 -0800207 WatchdogService wd_service;
208 // Stop the timer if the don't stop bit is not set
209 if (!(req.timer_use & wd_dont_stop))
210 {
211 wd_service.setEnabled(false);
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800212 }
213
William A. Kennington III52575252018-02-09 15:54:56 -0800214 // Set the action based on the request
Patrick Venture0b02be92018-08-31 11:55:55 -0700215 const auto ipmi_action =
216 static_cast<IpmiAction>(req.timer_action & wd_timeout_action_mask);
William A. Kennington IIIb638de22018-02-09 16:12:53 -0800217 wd_service.setExpireAction(ipmiActionToWdAction(ipmi_action));
William A. Kennington III52575252018-02-09 15:54:56 -0800218
Yong Li118907e2019-01-11 17:36:17 +0800219 const auto ipmiTimerUse =
220 static_cast<IpmiTimerUse>(req.timer_use & wdTimerUseMask);
221 wd_service.setTimerUse(ipmiTimerUseToWdTimerUse(ipmiTimerUse));
222
William A. Kennington III52575252018-02-09 15:54:56 -0800223 // Set the new interval and the time remaining deci -> mill seconds
224 const uint64_t interval = req.initial_countdown * 100;
225 wd_service.setInterval(interval);
226 wd_service.setTimeRemaining(interval);
227
William A. Kennington IIIde14a022018-02-09 16:11:18 -0800228 // Mark as initialized so that future resets behave correctly
229 wd_service.setInitialized(true);
230
William A. Kennington IIIbae471c2018-06-15 10:38:01 -0700231 lastCallSuccessful = true;
William A. Kennington III52575252018-02-09 15:54:56 -0800232 return IPMI_CC_OK;
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800233 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700234 catch (const std::domain_error&)
William A. Kennington III52575252018-02-09 15:54:56 -0800235 {
236 return IPMI_CC_INVALID_FIELD_REQUEST;
237 }
William A. Kennington III021b4c12018-05-10 11:12:51 -0700238 catch (const InternalFailure& e)
239 {
William A. Kennington IIIbae471c2018-06-15 10:38:01 -0700240 reportError();
William A. Kennington III021b4c12018-05-10 11:12:51 -0700241 return IPMI_CC_UNSPECIFIED_ERROR;
242 }
William A. Kennington III52575252018-02-09 15:54:56 -0800243 catch (const std::exception& e)
244 {
245 const std::string e_str = std::string("wd_set: ") + e.what();
246 log<level::ERR>(e_str.c_str());
William A. Kennington IIIbae471c2018-06-15 10:38:01 -0700247 reportError();
William A. Kennington III52575252018-02-09 15:54:56 -0800248 return IPMI_CC_UNSPECIFIED_ERROR;
249 }
250 catch (...)
251 {
252 log<level::ERR>("wd_set: Unknown Error");
William A. Kennington IIIbae471c2018-06-15 10:38:01 -0700253 reportError();
William A. Kennington III52575252018-02-09 15:54:56 -0800254 return IPMI_CC_UNSPECIFIED_ERROR;
255 }
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800256}
William A. Kennington III73f44512018-02-09 15:28:46 -0800257
258/** @brief Converts a DBUS Watchdog Action to IPMI defined action
259 * @param[in] wd_action The DBUS Watchdog Action
260 * @return The IpmiAction that the wd_action maps to
261 */
262IpmiAction wdActionToIpmiAction(WatchdogService::Action wd_action)
263{
Patrick Venture0b02be92018-08-31 11:55:55 -0700264 switch (wd_action)
William A. Kennington III73f44512018-02-09 15:28:46 -0800265 {
266 case WatchdogService::Action::None:
267 {
268 return IpmiAction::None;
269 }
270 case WatchdogService::Action::HardReset:
271 {
272 return IpmiAction::HardReset;
273 }
274 case WatchdogService::Action::PowerOff:
275 {
276 return IpmiAction::PowerOff;
277 }
278 case WatchdogService::Action::PowerCycle:
279 {
280 return IpmiAction::PowerCycle;
281 }
282 default:
283 {
284 // We have no method via IPMI to signal that the action is unknown
285 // or unmappable in some way.
286 // Just ignore the error and return NONE so the host can reconcile.
287 return IpmiAction::None;
288 }
289 }
290}
291
Yong Li118907e2019-01-11 17:36:17 +0800292IpmiTimerUse wdTimerUseToIpmiTimerUse(WatchdogService::TimerUse wdTimerUse)
293{
294 switch (wdTimerUse)
295 {
296 case WatchdogService::TimerUse::Reserved:
297 {
298 return IpmiTimerUse::Reserved;
299 }
300 case WatchdogService::TimerUse::BIOSFRB2:
301 {
302 return IpmiTimerUse::BIOSFRB2;
303 }
304 case WatchdogService::TimerUse::BIOSPOST:
305 {
306 return IpmiTimerUse::BIOSPOST;
307 }
308 case WatchdogService::TimerUse::OSLoad:
309 {
310 return IpmiTimerUse::OSLoad;
311 }
312
313 case WatchdogService::TimerUse::SMSOS:
314 {
315 return IpmiTimerUse::SMSOS;
316 }
317 case WatchdogService::TimerUse::OEM:
318 {
319 return IpmiTimerUse::OEM;
320 }
321 default:
322 {
323 return IpmiTimerUse::Reserved;
324 }
325 }
326}
327
Patrick Venture0b02be92018-08-31 11:55:55 -0700328struct wd_get_res
329{
William A. Kennington III73f44512018-02-09 15:28:46 -0800330 uint8_t timer_use;
331 uint8_t timer_action;
332 uint8_t pretimeout;
333 uint8_t expire_flags;
Patrick Venture0b02be92018-08-31 11:55:55 -0700334 uint16_t initial_countdown; // Little Endian (deciseconds)
335 uint16_t present_countdown; // Little Endian (deciseconds)
336} __attribute__((packed));
William A. Kennington III73f44512018-02-09 15:28:46 -0800337static_assert(sizeof(wd_get_res) == 8, "wd_get_res has invalid size.");
338static_assert(sizeof(wd_get_res) <= MAX_IPMI_BUFFER,
Patrick Venture0b02be92018-08-31 11:55:55 -0700339 "wd_get_res can't fit in response buffer.");
William A. Kennington III73f44512018-02-09 15:28:46 -0800340
341static constexpr uint8_t wd_dont_log = 0x1 << 7;
342static constexpr uint8_t wd_running = 0x1 << 6;
343
Patrick Venture0b02be92018-08-31 11:55:55 -0700344ipmi_ret_t ipmi_app_watchdog_get(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
345 ipmi_request_t request,
346 ipmi_response_t response,
347 ipmi_data_len_t data_len,
348 ipmi_context_t context)
William A. Kennington III73f44512018-02-09 15:28:46 -0800349{
350 // Assume we will fail and send no data outside the return code
351 *data_len = 0;
352
353 try
354 {
355 WatchdogService wd_service;
356 WatchdogService::Properties wd_prop = wd_service.getProperties();
357
358 // Build and return the response
359 wd_get_res res;
360 res.timer_use = wd_dont_log;
Patrick Venture0b02be92018-08-31 11:55:55 -0700361 res.timer_action =
362 static_cast<uint8_t>(wdActionToIpmiAction(wd_prop.expireAction));
Yong Lif7c9db02019-01-15 13:45:33 +0800363
364 // Interval and timeRemaining need converted from milli -> deci seconds
365 res.initial_countdown = htole16(wd_prop.interval / 100);
William A. Kennington III73f44512018-02-09 15:28:46 -0800366 if (wd_prop.enabled)
367 {
368 res.timer_use |= wd_running;
Yong Lif7c9db02019-01-15 13:45:33 +0800369 res.present_countdown = htole16(wd_prop.timeRemaining / 100);
370 }
371 else
372 {
373 res.present_countdown = res.initial_countdown;
William A. Kennington III73f44512018-02-09 15:28:46 -0800374 }
Yong Li118907e2019-01-11 17:36:17 +0800375
376 res.timer_use |=
377 static_cast<uint8_t>(wdTimerUseToIpmiTimerUse(wd_prop.timerUse));
378
William A. Kennington III73f44512018-02-09 15:28:46 -0800379 // TODO: Do something about having pretimeout support
380 res.pretimeout = 0;
381 res.expire_flags = 0;
William A. Kennington III73f44512018-02-09 15:28:46 -0800382 memcpy(response, &res, sizeof(res));
383 *data_len = sizeof(res);
William A. Kennington IIIbae471c2018-06-15 10:38:01 -0700384 lastCallSuccessful = true;
William A. Kennington III73f44512018-02-09 15:28:46 -0800385 return IPMI_CC_OK;
386 }
William A. Kennington III021b4c12018-05-10 11:12:51 -0700387 catch (const InternalFailure& e)
388 {
William A. Kennington IIIbae471c2018-06-15 10:38:01 -0700389 reportError();
William A. Kennington III021b4c12018-05-10 11:12:51 -0700390 return IPMI_CC_UNSPECIFIED_ERROR;
391 }
William A. Kennington III73f44512018-02-09 15:28:46 -0800392 catch (const std::exception& e)
393 {
394 const std::string e_str = std::string("wd_get: ") + e.what();
395 log<level::ERR>(e_str.c_str());
William A. Kennington IIIbae471c2018-06-15 10:38:01 -0700396 reportError();
William A. Kennington III73f44512018-02-09 15:28:46 -0800397 return IPMI_CC_UNSPECIFIED_ERROR;
398 }
399 catch (...)
400 {
401 log<level::ERR>("wd_get: Unknown Error");
William A. Kennington IIIbae471c2018-06-15 10:38:01 -0700402 reportError();
William A. Kennington III73f44512018-02-09 15:28:46 -0800403 return IPMI_CC_UNSPECIFIED_ERROR;
404 }
405}