blob: 1a5d19c3b1c67b459c3f499618faa1bcce5143b3 [file] [log] [blame]
Patrick Venture5e6ac712017-10-25 12:16:19 -07001#include "watchdog.hpp"
2
Patrick Venture0b02be92018-08-31 11:55:55 -07003#include "ipmid.hpp"
4#include "watchdog_service.hpp"
5
William A. Kennington III52575252018-02-09 15:54:56 -08006#include <endian.h>
William A. Kennington III194375f2018-12-14 02:14:33 -08007#include <ipmid/api.h>
Patrick Venture0b02be92018-08-31 11:55:55 -07008
9#include <cstdint>
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
Patrick Venture0b02be92018-08-31 11:55:55 -070042ipmi_ret_t ipmi_app_watchdog_reset(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
43 ipmi_request_t request,
44 ipmi_response_t response,
45 ipmi_data_len_t data_len,
46 ipmi_context_t context)
Patrick Venture5e6ac712017-10-25 12:16:19 -070047{
William A. Kennington III52575252018-02-09 15:54:56 -080048 // We never return data with this command so immediately get rid of it
Patrick Venture5e6ac712017-10-25 12:16:19 -070049 *data_len = 0;
50
William A. Kennington III52575252018-02-09 15:54:56 -080051 try
52 {
53 WatchdogService wd_service;
William A. Kennington III52575252018-02-09 15:54:56 -080054
William A. Kennington IIIde14a022018-02-09 16:11:18 -080055 // Notify the caller if we haven't initialized our timer yet
56 // so it can configure actions and timeouts
William A. Kennington III2ecf5122018-04-27 14:31:51 -070057 if (!wd_service.getInitialized())
William A. Kennington IIIde14a022018-02-09 16:11:18 -080058 {
William A. Kennington IIIbae471c2018-06-15 10:38:01 -070059 lastCallSuccessful = true;
William A. Kennington IIIde14a022018-02-09 16:11:18 -080060 return IPMI_WDOG_CC_NOT_INIT;
61 }
62
William A. Kennington III4b017a92018-04-27 14:31:08 -070063 // The ipmi standard dictates we enable the watchdog during reset
64 wd_service.resetTimeRemaining(true);
William A. Kennington IIIbae471c2018-06-15 10:38:01 -070065 lastCallSuccessful = true;
William A. Kennington III52575252018-02-09 15:54:56 -080066 return IPMI_CC_OK;
Patrick Venture5e6ac712017-10-25 12:16:19 -070067 }
William A. Kennington III021b4c12018-05-10 11:12:51 -070068 catch (const InternalFailure& e)
69 {
William A. Kennington IIIbae471c2018-06-15 10:38:01 -070070 reportError();
William A. Kennington III021b4c12018-05-10 11:12:51 -070071 return IPMI_CC_UNSPECIFIED_ERROR;
72 }
William A. Kennington III52575252018-02-09 15:54:56 -080073 catch (const std::exception& e)
74 {
75 const std::string e_str = std::string("wd_reset: ") + e.what();
76 log<level::ERR>(e_str.c_str());
William A. Kennington IIIbae471c2018-06-15 10:38:01 -070077 reportError();
William A. Kennington III52575252018-02-09 15:54:56 -080078 return IPMI_CC_UNSPECIFIED_ERROR;
William A. Kennington III5325f2c2018-01-08 15:17:09 -080079 }
William A. Kennington III52575252018-02-09 15:54:56 -080080 catch (...)
81 {
82 log<level::ERR>("wd_reset: Unknown Error");
William A. Kennington IIIbae471c2018-06-15 10:38:01 -070083 reportError();
William A. Kennington III52575252018-02-09 15:54:56 -080084 return IPMI_CC_UNSPECIFIED_ERROR;
William A. Kennington III5325f2c2018-01-08 15:17:09 -080085 }
Patrick Venture5e6ac712017-10-25 12:16:19 -070086}
William A. Kennington III61d5f7b2018-02-09 15:23:53 -080087
William A. Kennington III52575252018-02-09 15:54:56 -080088static constexpr uint8_t wd_dont_stop = 0x1 << 6;
89static constexpr uint8_t wd_timeout_action_mask = 0x3;
90
Yong Li118907e2019-01-11 17:36:17 +080091static constexpr uint8_t wdTimerUseMask = 0x7;
92
Patrick Venture0b02be92018-08-31 11:55:55 -070093enum class IpmiAction : uint8_t
94{
William A. Kennington III52575252018-02-09 15:54:56 -080095 None = 0x0,
96 HardReset = 0x1,
97 PowerOff = 0x2,
98 PowerCycle = 0x3,
99};
100
William A. Kennington IIIb638de22018-02-09 16:12:53 -0800101/** @brief Converts an IPMI Watchdog Action to DBUS defined action
102 * @param[in] ipmi_action The IPMI Watchdog Action
103 * @return The Watchdog Action that the ipmi_action maps to
104 */
105WatchdogService::Action ipmiActionToWdAction(IpmiAction ipmi_action)
106{
Patrick Venture0b02be92018-08-31 11:55:55 -0700107 switch (ipmi_action)
William A. Kennington IIIb638de22018-02-09 16:12:53 -0800108 {
109 case IpmiAction::None:
110 {
111 return WatchdogService::Action::None;
112 }
113 case IpmiAction::HardReset:
114 {
115 return WatchdogService::Action::HardReset;
116 }
117 case IpmiAction::PowerOff:
118 {
119 return WatchdogService::Action::PowerOff;
120 }
121 case IpmiAction::PowerCycle:
122 {
123 return WatchdogService::Action::PowerCycle;
124 }
125 default:
126 {
127 throw std::domain_error("IPMI Action is invalid");
128 }
129 }
130}
131
Yong Li118907e2019-01-11 17:36:17 +0800132enum class IpmiTimerUse : uint8_t
133{
134 Reserved = 0x0,
135 BIOSFRB2 = 0x1,
136 BIOSPOST = 0x2,
137 OSLoad = 0x3,
138 SMSOS = 0x4,
139 OEM = 0x5,
140};
141
142WatchdogService::TimerUse ipmiTimerUseToWdTimerUse(IpmiTimerUse ipmiTimerUse)
143{
144 switch (ipmiTimerUse)
145 {
146 case IpmiTimerUse::Reserved:
147 {
148 return WatchdogService::TimerUse::Reserved;
149 }
150 case IpmiTimerUse::BIOSFRB2:
151 {
152 return WatchdogService::TimerUse::BIOSFRB2;
153 }
154 case IpmiTimerUse::BIOSPOST:
155 {
156 return WatchdogService::TimerUse::BIOSPOST;
157 }
158 case IpmiTimerUse::OSLoad:
159 {
160 return WatchdogService::TimerUse::OSLoad;
161 }
162 case IpmiTimerUse::SMSOS:
163 {
164 return WatchdogService::TimerUse::SMSOS;
165 }
166 case IpmiTimerUse::OEM:
167 {
168 return WatchdogService::TimerUse::OEM;
169 }
170 default:
171 {
172 return WatchdogService::TimerUse::Reserved;
173 }
174 }
175}
176
Patrick Venture0b02be92018-08-31 11:55:55 -0700177struct wd_set_req
178{
William A. Kennington III52575252018-02-09 15:54:56 -0800179 uint8_t timer_use;
180 uint8_t timer_action;
Patrick Venture0b02be92018-08-31 11:55:55 -0700181 uint8_t pretimeout; // (seconds)
William A. Kennington III52575252018-02-09 15:54:56 -0800182 uint8_t expire_flags;
Patrick Venture0b02be92018-08-31 11:55:55 -0700183 uint16_t initial_countdown; // Little Endian (deciseconds)
184} __attribute__((packed));
William A. Kennington III52575252018-02-09 15:54:56 -0800185static_assert(sizeof(wd_set_req) == 6, "wd_set_req has invalid size.");
186static_assert(sizeof(wd_set_req) <= MAX_IPMI_BUFFER,
Patrick Venture0b02be92018-08-31 11:55:55 -0700187 "wd_get_res can't fit in request buffer.");
William A. Kennington III52575252018-02-09 15:54:56 -0800188
Patrick Venture0b02be92018-08-31 11:55:55 -0700189ipmi_ret_t ipmi_app_watchdog_set(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
190 ipmi_request_t request,
191 ipmi_response_t response,
192 ipmi_data_len_t data_len,
193 ipmi_context_t context)
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800194{
William A. Kennington III52575252018-02-09 15:54:56 -0800195 // Extract the request data
196 if (*data_len < sizeof(wd_set_req))
197 {
198 *data_len = 0;
199 return IPMI_CC_REQ_DATA_LEN_INVALID;
200 }
201 wd_set_req req;
202 memcpy(&req, request, sizeof(req));
203 req.initial_countdown = le16toh(req.initial_countdown);
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800204 *data_len = 0;
205
William A. Kennington III52575252018-02-09 15:54:56 -0800206 try
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800207 {
William A. Kennington III52575252018-02-09 15:54:56 -0800208 WatchdogService wd_service;
209 // Stop the timer if the don't stop bit is not set
210 if (!(req.timer_use & wd_dont_stop))
211 {
212 wd_service.setEnabled(false);
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800213 }
214
William A. Kennington III52575252018-02-09 15:54:56 -0800215 // Set the action based on the request
Patrick Venture0b02be92018-08-31 11:55:55 -0700216 const auto ipmi_action =
217 static_cast<IpmiAction>(req.timer_action & wd_timeout_action_mask);
William A. Kennington IIIb638de22018-02-09 16:12:53 -0800218 wd_service.setExpireAction(ipmiActionToWdAction(ipmi_action));
William A. Kennington III52575252018-02-09 15:54:56 -0800219
Yong Li118907e2019-01-11 17:36:17 +0800220 const auto ipmiTimerUse =
221 static_cast<IpmiTimerUse>(req.timer_use & wdTimerUseMask);
222 wd_service.setTimerUse(ipmiTimerUseToWdTimerUse(ipmiTimerUse));
223
William A. Kennington III52575252018-02-09 15:54:56 -0800224 // Set the new interval and the time remaining deci -> mill seconds
225 const uint64_t interval = req.initial_countdown * 100;
226 wd_service.setInterval(interval);
227 wd_service.setTimeRemaining(interval);
228
William A. Kennington IIIde14a022018-02-09 16:11:18 -0800229 // Mark as initialized so that future resets behave correctly
230 wd_service.setInitialized(true);
231
William A. Kennington IIIbae471c2018-06-15 10:38:01 -0700232 lastCallSuccessful = true;
William A. Kennington III52575252018-02-09 15:54:56 -0800233 return IPMI_CC_OK;
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800234 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700235 catch (const std::domain_error&)
William A. Kennington III52575252018-02-09 15:54:56 -0800236 {
237 return IPMI_CC_INVALID_FIELD_REQUEST;
238 }
William A. Kennington III021b4c12018-05-10 11:12:51 -0700239 catch (const InternalFailure& e)
240 {
William A. Kennington IIIbae471c2018-06-15 10:38:01 -0700241 reportError();
William A. Kennington III021b4c12018-05-10 11:12:51 -0700242 return IPMI_CC_UNSPECIFIED_ERROR;
243 }
William A. Kennington III52575252018-02-09 15:54:56 -0800244 catch (const std::exception& e)
245 {
246 const std::string e_str = std::string("wd_set: ") + e.what();
247 log<level::ERR>(e_str.c_str());
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 }
251 catch (...)
252 {
253 log<level::ERR>("wd_set: Unknown Error");
William A. Kennington IIIbae471c2018-06-15 10:38:01 -0700254 reportError();
William A. Kennington III52575252018-02-09 15:54:56 -0800255 return IPMI_CC_UNSPECIFIED_ERROR;
256 }
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800257}
William A. Kennington III73f44512018-02-09 15:28:46 -0800258
259/** @brief Converts a DBUS Watchdog Action to IPMI defined action
260 * @param[in] wd_action The DBUS Watchdog Action
261 * @return The IpmiAction that the wd_action maps to
262 */
263IpmiAction wdActionToIpmiAction(WatchdogService::Action wd_action)
264{
Patrick Venture0b02be92018-08-31 11:55:55 -0700265 switch (wd_action)
William A. Kennington III73f44512018-02-09 15:28:46 -0800266 {
267 case WatchdogService::Action::None:
268 {
269 return IpmiAction::None;
270 }
271 case WatchdogService::Action::HardReset:
272 {
273 return IpmiAction::HardReset;
274 }
275 case WatchdogService::Action::PowerOff:
276 {
277 return IpmiAction::PowerOff;
278 }
279 case WatchdogService::Action::PowerCycle:
280 {
281 return IpmiAction::PowerCycle;
282 }
283 default:
284 {
285 // We have no method via IPMI to signal that the action is unknown
286 // or unmappable in some way.
287 // Just ignore the error and return NONE so the host can reconcile.
288 return IpmiAction::None;
289 }
290 }
291}
292
Yong Li118907e2019-01-11 17:36:17 +0800293IpmiTimerUse wdTimerUseToIpmiTimerUse(WatchdogService::TimerUse wdTimerUse)
294{
295 switch (wdTimerUse)
296 {
297 case WatchdogService::TimerUse::Reserved:
298 {
299 return IpmiTimerUse::Reserved;
300 }
301 case WatchdogService::TimerUse::BIOSFRB2:
302 {
303 return IpmiTimerUse::BIOSFRB2;
304 }
305 case WatchdogService::TimerUse::BIOSPOST:
306 {
307 return IpmiTimerUse::BIOSPOST;
308 }
309 case WatchdogService::TimerUse::OSLoad:
310 {
311 return IpmiTimerUse::OSLoad;
312 }
313
314 case WatchdogService::TimerUse::SMSOS:
315 {
316 return IpmiTimerUse::SMSOS;
317 }
318 case WatchdogService::TimerUse::OEM:
319 {
320 return IpmiTimerUse::OEM;
321 }
322 default:
323 {
324 return IpmiTimerUse::Reserved;
325 }
326 }
327}
328
Patrick Venture0b02be92018-08-31 11:55:55 -0700329struct wd_get_res
330{
William A. Kennington III73f44512018-02-09 15:28:46 -0800331 uint8_t timer_use;
332 uint8_t timer_action;
333 uint8_t pretimeout;
334 uint8_t expire_flags;
Patrick Venture0b02be92018-08-31 11:55:55 -0700335 uint16_t initial_countdown; // Little Endian (deciseconds)
336 uint16_t present_countdown; // Little Endian (deciseconds)
337} __attribute__((packed));
William A. Kennington III73f44512018-02-09 15:28:46 -0800338static_assert(sizeof(wd_get_res) == 8, "wd_get_res has invalid size.");
339static_assert(sizeof(wd_get_res) <= MAX_IPMI_BUFFER,
Patrick Venture0b02be92018-08-31 11:55:55 -0700340 "wd_get_res can't fit in response buffer.");
William A. Kennington III73f44512018-02-09 15:28:46 -0800341
342static constexpr uint8_t wd_dont_log = 0x1 << 7;
343static constexpr uint8_t wd_running = 0x1 << 6;
344
Patrick Venture0b02be92018-08-31 11:55:55 -0700345ipmi_ret_t ipmi_app_watchdog_get(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
346 ipmi_request_t request,
347 ipmi_response_t response,
348 ipmi_data_len_t data_len,
349 ipmi_context_t context)
William A. Kennington III73f44512018-02-09 15:28:46 -0800350{
351 // Assume we will fail and send no data outside the return code
352 *data_len = 0;
353
354 try
355 {
356 WatchdogService wd_service;
357 WatchdogService::Properties wd_prop = wd_service.getProperties();
358
359 // Build and return the response
360 wd_get_res res;
361 res.timer_use = wd_dont_log;
Patrick Venture0b02be92018-08-31 11:55:55 -0700362 res.timer_action =
363 static_cast<uint8_t>(wdActionToIpmiAction(wd_prop.expireAction));
Yong Lif7c9db02019-01-15 13:45:33 +0800364
365 // Interval and timeRemaining need converted from milli -> deci seconds
366 res.initial_countdown = htole16(wd_prop.interval / 100);
William A. Kennington III73f44512018-02-09 15:28:46 -0800367 if (wd_prop.enabled)
368 {
369 res.timer_use |= wd_running;
Yong Lif7c9db02019-01-15 13:45:33 +0800370 res.present_countdown = htole16(wd_prop.timeRemaining / 100);
371 }
372 else
373 {
374 res.present_countdown = res.initial_countdown;
William A. Kennington III73f44512018-02-09 15:28:46 -0800375 }
Yong Li118907e2019-01-11 17:36:17 +0800376
377 res.timer_use |=
378 static_cast<uint8_t>(wdTimerUseToIpmiTimerUse(wd_prop.timerUse));
379
William A. Kennington III73f44512018-02-09 15:28:46 -0800380 // TODO: Do something about having pretimeout support
381 res.pretimeout = 0;
382 res.expire_flags = 0;
William A. Kennington III73f44512018-02-09 15:28:46 -0800383 memcpy(response, &res, sizeof(res));
384 *data_len = sizeof(res);
William A. Kennington IIIbae471c2018-06-15 10:38:01 -0700385 lastCallSuccessful = true;
William A. Kennington III73f44512018-02-09 15:28:46 -0800386 return IPMI_CC_OK;
387 }
William A. Kennington III021b4c12018-05-10 11:12:51 -0700388 catch (const InternalFailure& e)
389 {
William A. Kennington IIIbae471c2018-06-15 10:38:01 -0700390 reportError();
William A. Kennington III021b4c12018-05-10 11:12:51 -0700391 return IPMI_CC_UNSPECIFIED_ERROR;
392 }
William A. Kennington III73f44512018-02-09 15:28:46 -0800393 catch (const std::exception& e)
394 {
395 const std::string e_str = std::string("wd_get: ") + e.what();
396 log<level::ERR>(e_str.c_str());
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 catch (...)
401 {
402 log<level::ERR>("wd_get: Unknown Error");
William A. Kennington IIIbae471c2018-06-15 10:38:01 -0700403 reportError();
William A. Kennington III73f44512018-02-09 15:28:46 -0800404 return IPMI_CC_UNSPECIFIED_ERROR;
405 }
406}