blob: f6818cb6583789d8a72763d28862589dbffba40b [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>
Patrick Venture0b02be92018-08-31 11:55:55 -07007
8#include <cstdint>
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 III52575252018-02-09 15:54:56 -080015#include "host-ipmid/ipmid-api.h"
Patrick Venture5e6ac712017-10-25 12:16:19 -070016
William A. Kennington IIIbae471c2018-06-15 10:38:01 -070017using phosphor::logging::commit;
William A. Kennington III52575252018-02-09 15:54:56 -080018using phosphor::logging::level;
19using phosphor::logging::log;
William A. Kennington III021b4c12018-05-10 11:12:51 -070020using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
Patrick Venture5e6ac712017-10-25 12:16:19 -070021
William A. Kennington IIIbae471c2018-06-15 10:38:01 -070022static bool lastCallSuccessful = false;
23
24void reportError()
25{
26 // We don't want to fill the SEL with errors if the daemon dies and doesn't
27 // come back but the watchdog keeps on ticking. Instead, we only report the
28 // error if we haven't reported one since the last successful call
29 if (!lastCallSuccessful)
30 {
31 return;
32 }
33 lastCallSuccessful = false;
34
35 // TODO: This slow down the end of the IPMI transaction waiting
36 // for the commit to finish. commit<>() can take at least 5 seconds
37 // to complete. 5s is very slow for an IPMI command and ends up
38 // congesting the IPMI channel needlessly, especially if the watchdog
39 // is ticking fairly quickly and we have some transient issues.
40 commit<InternalFailure>();
41}
42
Patrick Venture0b02be92018-08-31 11:55:55 -070043ipmi_ret_t ipmi_app_watchdog_reset(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
44 ipmi_request_t request,
45 ipmi_response_t response,
46 ipmi_data_len_t data_len,
47 ipmi_context_t context)
Patrick Venture5e6ac712017-10-25 12:16:19 -070048{
William A. Kennington III52575252018-02-09 15:54:56 -080049 // We never return data with this command so immediately get rid of it
Patrick Venture5e6ac712017-10-25 12:16:19 -070050 *data_len = 0;
51
William A. Kennington III52575252018-02-09 15:54:56 -080052 try
53 {
54 WatchdogService wd_service;
William A. Kennington III52575252018-02-09 15:54:56 -080055
William A. Kennington IIIde14a022018-02-09 16:11:18 -080056 // Notify the caller if we haven't initialized our timer yet
57 // so it can configure actions and timeouts
William A. Kennington III2ecf5122018-04-27 14:31:51 -070058 if (!wd_service.getInitialized())
William A. Kennington IIIde14a022018-02-09 16:11:18 -080059 {
William A. Kennington IIIbae471c2018-06-15 10:38:01 -070060 lastCallSuccessful = true;
William A. Kennington IIIde14a022018-02-09 16:11:18 -080061 return IPMI_WDOG_CC_NOT_INIT;
62 }
63
William A. Kennington III4b017a92018-04-27 14:31:08 -070064 // The ipmi standard dictates we enable the watchdog during reset
65 wd_service.resetTimeRemaining(true);
William A. Kennington IIIbae471c2018-06-15 10:38:01 -070066 lastCallSuccessful = true;
William A. Kennington III52575252018-02-09 15:54:56 -080067 return IPMI_CC_OK;
Patrick Venture5e6ac712017-10-25 12:16:19 -070068 }
William A. Kennington III021b4c12018-05-10 11:12:51 -070069 catch (const InternalFailure& e)
70 {
William A. Kennington IIIbae471c2018-06-15 10:38:01 -070071 reportError();
William A. Kennington III021b4c12018-05-10 11:12:51 -070072 return IPMI_CC_UNSPECIFIED_ERROR;
73 }
William A. Kennington III52575252018-02-09 15:54:56 -080074 catch (const std::exception& e)
75 {
76 const std::string e_str = std::string("wd_reset: ") + e.what();
77 log<level::ERR>(e_str.c_str());
William A. Kennington IIIbae471c2018-06-15 10:38:01 -070078 reportError();
William A. Kennington III52575252018-02-09 15:54:56 -080079 return IPMI_CC_UNSPECIFIED_ERROR;
William A. Kennington III5325f2c2018-01-08 15:17:09 -080080 }
William A. Kennington III52575252018-02-09 15:54:56 -080081 catch (...)
82 {
83 log<level::ERR>("wd_reset: Unknown Error");
William A. Kennington IIIbae471c2018-06-15 10:38:01 -070084 reportError();
William A. Kennington III52575252018-02-09 15:54:56 -080085 return IPMI_CC_UNSPECIFIED_ERROR;
William A. Kennington III5325f2c2018-01-08 15:17:09 -080086 }
Patrick Venture5e6ac712017-10-25 12:16:19 -070087}
William A. Kennington III61d5f7b2018-02-09 15:23:53 -080088
William A. Kennington III52575252018-02-09 15:54:56 -080089static constexpr uint8_t wd_dont_stop = 0x1 << 6;
90static constexpr uint8_t wd_timeout_action_mask = 0x3;
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
Patrick Venture0b02be92018-08-31 11:55:55 -0700131struct wd_set_req
132{
William A. Kennington III52575252018-02-09 15:54:56 -0800133 uint8_t timer_use;
134 uint8_t timer_action;
Patrick Venture0b02be92018-08-31 11:55:55 -0700135 uint8_t pretimeout; // (seconds)
William A. Kennington III52575252018-02-09 15:54:56 -0800136 uint8_t expire_flags;
Patrick Venture0b02be92018-08-31 11:55:55 -0700137 uint16_t initial_countdown; // Little Endian (deciseconds)
138} __attribute__((packed));
William A. Kennington III52575252018-02-09 15:54:56 -0800139static_assert(sizeof(wd_set_req) == 6, "wd_set_req has invalid size.");
140static_assert(sizeof(wd_set_req) <= MAX_IPMI_BUFFER,
Patrick Venture0b02be92018-08-31 11:55:55 -0700141 "wd_get_res can't fit in request buffer.");
William A. Kennington III52575252018-02-09 15:54:56 -0800142
Patrick Venture0b02be92018-08-31 11:55:55 -0700143ipmi_ret_t ipmi_app_watchdog_set(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
144 ipmi_request_t request,
145 ipmi_response_t response,
146 ipmi_data_len_t data_len,
147 ipmi_context_t context)
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800148{
William A. Kennington III52575252018-02-09 15:54:56 -0800149 // Extract the request data
150 if (*data_len < sizeof(wd_set_req))
151 {
152 *data_len = 0;
153 return IPMI_CC_REQ_DATA_LEN_INVALID;
154 }
155 wd_set_req req;
156 memcpy(&req, request, sizeof(req));
157 req.initial_countdown = le16toh(req.initial_countdown);
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800158 *data_len = 0;
159
William A. Kennington III52575252018-02-09 15:54:56 -0800160 try
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800161 {
William A. Kennington III52575252018-02-09 15:54:56 -0800162 WatchdogService wd_service;
163 // Stop the timer if the don't stop bit is not set
164 if (!(req.timer_use & wd_dont_stop))
165 {
166 wd_service.setEnabled(false);
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800167 }
168
William A. Kennington III52575252018-02-09 15:54:56 -0800169 // Set the action based on the request
Patrick Venture0b02be92018-08-31 11:55:55 -0700170 const auto ipmi_action =
171 static_cast<IpmiAction>(req.timer_action & wd_timeout_action_mask);
William A. Kennington IIIb638de22018-02-09 16:12:53 -0800172 wd_service.setExpireAction(ipmiActionToWdAction(ipmi_action));
William A. Kennington III52575252018-02-09 15:54:56 -0800173
174 // Set the new interval and the time remaining deci -> mill seconds
175 const uint64_t interval = req.initial_countdown * 100;
176 wd_service.setInterval(interval);
177 wd_service.setTimeRemaining(interval);
178
William A. Kennington IIIde14a022018-02-09 16:11:18 -0800179 // Mark as initialized so that future resets behave correctly
180 wd_service.setInitialized(true);
181
William A. Kennington IIIbae471c2018-06-15 10:38:01 -0700182 lastCallSuccessful = true;
William A. Kennington III52575252018-02-09 15:54:56 -0800183 return IPMI_CC_OK;
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800184 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700185 catch (const std::domain_error&)
William A. Kennington III52575252018-02-09 15:54:56 -0800186 {
187 return IPMI_CC_INVALID_FIELD_REQUEST;
188 }
William A. Kennington III021b4c12018-05-10 11:12:51 -0700189 catch (const InternalFailure& e)
190 {
William A. Kennington IIIbae471c2018-06-15 10:38:01 -0700191 reportError();
William A. Kennington III021b4c12018-05-10 11:12:51 -0700192 return IPMI_CC_UNSPECIFIED_ERROR;
193 }
William A. Kennington III52575252018-02-09 15:54:56 -0800194 catch (const std::exception& e)
195 {
196 const std::string e_str = std::string("wd_set: ") + e.what();
197 log<level::ERR>(e_str.c_str());
William A. Kennington IIIbae471c2018-06-15 10:38:01 -0700198 reportError();
William A. Kennington III52575252018-02-09 15:54:56 -0800199 return IPMI_CC_UNSPECIFIED_ERROR;
200 }
201 catch (...)
202 {
203 log<level::ERR>("wd_set: Unknown Error");
William A. Kennington IIIbae471c2018-06-15 10:38:01 -0700204 reportError();
William A. Kennington III52575252018-02-09 15:54:56 -0800205 return IPMI_CC_UNSPECIFIED_ERROR;
206 }
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800207}
William A. Kennington III73f44512018-02-09 15:28:46 -0800208
209/** @brief Converts a DBUS Watchdog Action to IPMI defined action
210 * @param[in] wd_action The DBUS Watchdog Action
211 * @return The IpmiAction that the wd_action maps to
212 */
213IpmiAction wdActionToIpmiAction(WatchdogService::Action wd_action)
214{
Patrick Venture0b02be92018-08-31 11:55:55 -0700215 switch (wd_action)
William A. Kennington III73f44512018-02-09 15:28:46 -0800216 {
217 case WatchdogService::Action::None:
218 {
219 return IpmiAction::None;
220 }
221 case WatchdogService::Action::HardReset:
222 {
223 return IpmiAction::HardReset;
224 }
225 case WatchdogService::Action::PowerOff:
226 {
227 return IpmiAction::PowerOff;
228 }
229 case WatchdogService::Action::PowerCycle:
230 {
231 return IpmiAction::PowerCycle;
232 }
233 default:
234 {
235 // We have no method via IPMI to signal that the action is unknown
236 // or unmappable in some way.
237 // Just ignore the error and return NONE so the host can reconcile.
238 return IpmiAction::None;
239 }
240 }
241}
242
Patrick Venture0b02be92018-08-31 11:55:55 -0700243struct wd_get_res
244{
William A. Kennington III73f44512018-02-09 15:28:46 -0800245 uint8_t timer_use;
246 uint8_t timer_action;
247 uint8_t pretimeout;
248 uint8_t expire_flags;
Patrick Venture0b02be92018-08-31 11:55:55 -0700249 uint16_t initial_countdown; // Little Endian (deciseconds)
250 uint16_t present_countdown; // Little Endian (deciseconds)
251} __attribute__((packed));
William A. Kennington III73f44512018-02-09 15:28:46 -0800252static_assert(sizeof(wd_get_res) == 8, "wd_get_res has invalid size.");
253static_assert(sizeof(wd_get_res) <= MAX_IPMI_BUFFER,
Patrick Venture0b02be92018-08-31 11:55:55 -0700254 "wd_get_res can't fit in response buffer.");
William A. Kennington III73f44512018-02-09 15:28:46 -0800255
256static constexpr uint8_t wd_dont_log = 0x1 << 7;
257static constexpr uint8_t wd_running = 0x1 << 6;
258
Patrick Venture0b02be92018-08-31 11:55:55 -0700259ipmi_ret_t ipmi_app_watchdog_get(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
260 ipmi_request_t request,
261 ipmi_response_t response,
262 ipmi_data_len_t data_len,
263 ipmi_context_t context)
William A. Kennington III73f44512018-02-09 15:28:46 -0800264{
265 // Assume we will fail and send no data outside the return code
266 *data_len = 0;
267
268 try
269 {
270 WatchdogService wd_service;
271 WatchdogService::Properties wd_prop = wd_service.getProperties();
272
273 // Build and return the response
274 wd_get_res res;
275 res.timer_use = wd_dont_log;
Patrick Venture0b02be92018-08-31 11:55:55 -0700276 res.timer_action =
277 static_cast<uint8_t>(wdActionToIpmiAction(wd_prop.expireAction));
William A. Kennington III73f44512018-02-09 15:28:46 -0800278 if (wd_prop.enabled)
279 {
280 res.timer_use |= wd_running;
281 }
282 // TODO: Do something about having pretimeout support
283 res.pretimeout = 0;
284 res.expire_flags = 0;
285 // Interval and timeRemaining need converted from milli -> deci seconds
286 res.initial_countdown = htole16(wd_prop.interval / 100);
287 res.present_countdown = htole16(wd_prop.timeRemaining / 100);
288
289 memcpy(response, &res, sizeof(res));
290 *data_len = sizeof(res);
William A. Kennington IIIbae471c2018-06-15 10:38:01 -0700291 lastCallSuccessful = true;
William A. Kennington III73f44512018-02-09 15:28:46 -0800292 return IPMI_CC_OK;
293 }
William A. Kennington III021b4c12018-05-10 11:12:51 -0700294 catch (const InternalFailure& e)
295 {
William A. Kennington IIIbae471c2018-06-15 10:38:01 -0700296 reportError();
William A. Kennington III021b4c12018-05-10 11:12:51 -0700297 return IPMI_CC_UNSPECIFIED_ERROR;
298 }
William A. Kennington III73f44512018-02-09 15:28:46 -0800299 catch (const std::exception& e)
300 {
301 const std::string e_str = std::string("wd_get: ") + e.what();
302 log<level::ERR>(e_str.c_str());
William A. Kennington IIIbae471c2018-06-15 10:38:01 -0700303 reportError();
William A. Kennington III73f44512018-02-09 15:28:46 -0800304 return IPMI_CC_UNSPECIFIED_ERROR;
305 }
306 catch (...)
307 {
308 log<level::ERR>("wd_get: Unknown Error");
William A. Kennington IIIbae471c2018-06-15 10:38:01 -0700309 reportError();
William A. Kennington III73f44512018-02-09 15:28:46 -0800310 return IPMI_CC_UNSPECIFIED_ERROR;
311 }
312}