blob: 7ca465cb51f1e25bc2debec4d86f3e6049c32cd9 [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
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
Patrick Venture0b02be92018-08-31 11:55:55 -0700130struct wd_set_req
131{
William A. Kennington III52575252018-02-09 15:54:56 -0800132 uint8_t timer_use;
133 uint8_t timer_action;
Patrick Venture0b02be92018-08-31 11:55:55 -0700134 uint8_t pretimeout; // (seconds)
William A. Kennington III52575252018-02-09 15:54:56 -0800135 uint8_t expire_flags;
Patrick Venture0b02be92018-08-31 11:55:55 -0700136 uint16_t initial_countdown; // Little Endian (deciseconds)
137} __attribute__((packed));
William A. Kennington III52575252018-02-09 15:54:56 -0800138static_assert(sizeof(wd_set_req) == 6, "wd_set_req has invalid size.");
139static_assert(sizeof(wd_set_req) <= MAX_IPMI_BUFFER,
Patrick Venture0b02be92018-08-31 11:55:55 -0700140 "wd_get_res can't fit in request buffer.");
William A. Kennington III52575252018-02-09 15:54:56 -0800141
Patrick Venture0b02be92018-08-31 11:55:55 -0700142ipmi_ret_t ipmi_app_watchdog_set(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
143 ipmi_request_t request,
144 ipmi_response_t response,
145 ipmi_data_len_t data_len,
146 ipmi_context_t context)
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800147{
William A. Kennington III52575252018-02-09 15:54:56 -0800148 // Extract the request data
149 if (*data_len < sizeof(wd_set_req))
150 {
151 *data_len = 0;
152 return IPMI_CC_REQ_DATA_LEN_INVALID;
153 }
154 wd_set_req req;
155 memcpy(&req, request, sizeof(req));
156 req.initial_countdown = le16toh(req.initial_countdown);
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800157 *data_len = 0;
158
William A. Kennington III52575252018-02-09 15:54:56 -0800159 try
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800160 {
William A. Kennington III52575252018-02-09 15:54:56 -0800161 WatchdogService wd_service;
162 // Stop the timer if the don't stop bit is not set
163 if (!(req.timer_use & wd_dont_stop))
164 {
165 wd_service.setEnabled(false);
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800166 }
167
William A. Kennington III52575252018-02-09 15:54:56 -0800168 // Set the action based on the request
Patrick Venture0b02be92018-08-31 11:55:55 -0700169 const auto ipmi_action =
170 static_cast<IpmiAction>(req.timer_action & wd_timeout_action_mask);
William A. Kennington IIIb638de22018-02-09 16:12:53 -0800171 wd_service.setExpireAction(ipmiActionToWdAction(ipmi_action));
William A. Kennington III52575252018-02-09 15:54:56 -0800172
173 // Set the new interval and the time remaining deci -> mill seconds
174 const uint64_t interval = req.initial_countdown * 100;
175 wd_service.setInterval(interval);
176 wd_service.setTimeRemaining(interval);
177
William A. Kennington IIIde14a022018-02-09 16:11:18 -0800178 // Mark as initialized so that future resets behave correctly
179 wd_service.setInitialized(true);
180
William A. Kennington IIIbae471c2018-06-15 10:38:01 -0700181 lastCallSuccessful = true;
William A. Kennington III52575252018-02-09 15:54:56 -0800182 return IPMI_CC_OK;
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800183 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700184 catch (const std::domain_error&)
William A. Kennington III52575252018-02-09 15:54:56 -0800185 {
186 return IPMI_CC_INVALID_FIELD_REQUEST;
187 }
William A. Kennington III021b4c12018-05-10 11:12:51 -0700188 catch (const InternalFailure& e)
189 {
William A. Kennington IIIbae471c2018-06-15 10:38:01 -0700190 reportError();
William A. Kennington III021b4c12018-05-10 11:12:51 -0700191 return IPMI_CC_UNSPECIFIED_ERROR;
192 }
William A. Kennington III52575252018-02-09 15:54:56 -0800193 catch (const std::exception& e)
194 {
195 const std::string e_str = std::string("wd_set: ") + e.what();
196 log<level::ERR>(e_str.c_str());
William A. Kennington IIIbae471c2018-06-15 10:38:01 -0700197 reportError();
William A. Kennington III52575252018-02-09 15:54:56 -0800198 return IPMI_CC_UNSPECIFIED_ERROR;
199 }
200 catch (...)
201 {
202 log<level::ERR>("wd_set: Unknown Error");
William A. Kennington IIIbae471c2018-06-15 10:38:01 -0700203 reportError();
William A. Kennington III52575252018-02-09 15:54:56 -0800204 return IPMI_CC_UNSPECIFIED_ERROR;
205 }
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800206}
William A. Kennington III73f44512018-02-09 15:28:46 -0800207
208/** @brief Converts a DBUS Watchdog Action to IPMI defined action
209 * @param[in] wd_action The DBUS Watchdog Action
210 * @return The IpmiAction that the wd_action maps to
211 */
212IpmiAction wdActionToIpmiAction(WatchdogService::Action wd_action)
213{
Patrick Venture0b02be92018-08-31 11:55:55 -0700214 switch (wd_action)
William A. Kennington III73f44512018-02-09 15:28:46 -0800215 {
216 case WatchdogService::Action::None:
217 {
218 return IpmiAction::None;
219 }
220 case WatchdogService::Action::HardReset:
221 {
222 return IpmiAction::HardReset;
223 }
224 case WatchdogService::Action::PowerOff:
225 {
226 return IpmiAction::PowerOff;
227 }
228 case WatchdogService::Action::PowerCycle:
229 {
230 return IpmiAction::PowerCycle;
231 }
232 default:
233 {
234 // We have no method via IPMI to signal that the action is unknown
235 // or unmappable in some way.
236 // Just ignore the error and return NONE so the host can reconcile.
237 return IpmiAction::None;
238 }
239 }
240}
241
Patrick Venture0b02be92018-08-31 11:55:55 -0700242struct wd_get_res
243{
William A. Kennington III73f44512018-02-09 15:28:46 -0800244 uint8_t timer_use;
245 uint8_t timer_action;
246 uint8_t pretimeout;
247 uint8_t expire_flags;
Patrick Venture0b02be92018-08-31 11:55:55 -0700248 uint16_t initial_countdown; // Little Endian (deciseconds)
249 uint16_t present_countdown; // Little Endian (deciseconds)
250} __attribute__((packed));
William A. Kennington III73f44512018-02-09 15:28:46 -0800251static_assert(sizeof(wd_get_res) == 8, "wd_get_res has invalid size.");
252static_assert(sizeof(wd_get_res) <= MAX_IPMI_BUFFER,
Patrick Venture0b02be92018-08-31 11:55:55 -0700253 "wd_get_res can't fit in response buffer.");
William A. Kennington III73f44512018-02-09 15:28:46 -0800254
255static constexpr uint8_t wd_dont_log = 0x1 << 7;
256static constexpr uint8_t wd_running = 0x1 << 6;
257
Patrick Venture0b02be92018-08-31 11:55:55 -0700258ipmi_ret_t ipmi_app_watchdog_get(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
259 ipmi_request_t request,
260 ipmi_response_t response,
261 ipmi_data_len_t data_len,
262 ipmi_context_t context)
William A. Kennington III73f44512018-02-09 15:28:46 -0800263{
264 // Assume we will fail and send no data outside the return code
265 *data_len = 0;
266
267 try
268 {
269 WatchdogService wd_service;
270 WatchdogService::Properties wd_prop = wd_service.getProperties();
271
272 // Build and return the response
273 wd_get_res res;
274 res.timer_use = wd_dont_log;
Patrick Venture0b02be92018-08-31 11:55:55 -0700275 res.timer_action =
276 static_cast<uint8_t>(wdActionToIpmiAction(wd_prop.expireAction));
William A. Kennington III73f44512018-02-09 15:28:46 -0800277 if (wd_prop.enabled)
278 {
279 res.timer_use |= wd_running;
280 }
281 // TODO: Do something about having pretimeout support
282 res.pretimeout = 0;
283 res.expire_flags = 0;
284 // Interval and timeRemaining need converted from milli -> deci seconds
285 res.initial_countdown = htole16(wd_prop.interval / 100);
286 res.present_countdown = htole16(wd_prop.timeRemaining / 100);
287
288 memcpy(response, &res, sizeof(res));
289 *data_len = sizeof(res);
William A. Kennington IIIbae471c2018-06-15 10:38:01 -0700290 lastCallSuccessful = true;
William A. Kennington III73f44512018-02-09 15:28:46 -0800291 return IPMI_CC_OK;
292 }
William A. Kennington III021b4c12018-05-10 11:12:51 -0700293 catch (const InternalFailure& e)
294 {
William A. Kennington IIIbae471c2018-06-15 10:38:01 -0700295 reportError();
William A. Kennington III021b4c12018-05-10 11:12:51 -0700296 return IPMI_CC_UNSPECIFIED_ERROR;
297 }
William A. Kennington III73f44512018-02-09 15:28:46 -0800298 catch (const std::exception& e)
299 {
300 const std::string e_str = std::string("wd_get: ") + e.what();
301 log<level::ERR>(e_str.c_str());
William A. Kennington IIIbae471c2018-06-15 10:38:01 -0700302 reportError();
William A. Kennington III73f44512018-02-09 15:28:46 -0800303 return IPMI_CC_UNSPECIFIED_ERROR;
304 }
305 catch (...)
306 {
307 log<level::ERR>("wd_get: Unknown Error");
William A. Kennington IIIbae471c2018-06-15 10:38:01 -0700308 reportError();
William A. Kennington III73f44512018-02-09 15:28:46 -0800309 return IPMI_CC_UNSPECIFIED_ERROR;
310 }
311}