blob: dbd7426bd3abc35900746bf473060a491ee07f9a [file] [log] [blame]
Patrick Venture5e6ac712017-10-25 12:16:19 -07001#include "watchdog.hpp"
2
William A. Kennington III52575252018-02-09 15:54:56 -08003#include <cstdint>
4#include <endian.h>
William A. Kennington III021b4c12018-05-10 11:12:51 -07005#include <phosphor-logging/elog.hpp>
6#include <phosphor-logging/elog-errors.hpp>
William A. Kennington III52575252018-02-09 15:54:56 -08007#include <phosphor-logging/log.hpp>
8#include <string>
William A. Kennington III021b4c12018-05-10 11:12:51 -07009#include <xyz/openbmc_project/Common/error.hpp>
Patrick Venture894571d2017-11-09 14:46:54 -080010
William A. Kennington III52575252018-02-09 15:54:56 -080011#include "watchdog_service.hpp"
12#include "host-ipmid/ipmid-api.h"
13#include "ipmid.hpp"
Patrick Venture5e6ac712017-10-25 12:16:19 -070014
William A. Kennington III52575252018-02-09 15:54:56 -080015using phosphor::logging::level;
16using phosphor::logging::log;
William A. Kennington III021b4c12018-05-10 11:12:51 -070017using phosphor::logging::report;
18using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
Patrick Venture5e6ac712017-10-25 12:16:19 -070019
William A. Kennington III61d5f7b2018-02-09 15:23:53 -080020ipmi_ret_t ipmi_app_watchdog_reset(
Patrick Venture894571d2017-11-09 14:46:54 -080021 ipmi_netfn_t netfn,
22 ipmi_cmd_t cmd,
23 ipmi_request_t request,
24 ipmi_response_t response,
25 ipmi_data_len_t data_len,
26 ipmi_context_t context)
Patrick Venture5e6ac712017-10-25 12:16:19 -070027{
William A. Kennington III52575252018-02-09 15:54:56 -080028 // We never return data with this command so immediately get rid of it
Patrick Venture5e6ac712017-10-25 12:16:19 -070029 *data_len = 0;
30
William A. Kennington III52575252018-02-09 15:54:56 -080031 try
32 {
33 WatchdogService wd_service;
34 WatchdogService::Properties wd_prop = wd_service.getProperties();
35
William A. Kennington IIIde14a022018-02-09 16:11:18 -080036 // Notify the caller if we haven't initialized our timer yet
37 // so it can configure actions and timeouts
38 if (!wd_prop.initialized)
39 {
40 return IPMI_WDOG_CC_NOT_INIT;
41 }
42
William A. Kennington III4b017a92018-04-27 14:31:08 -070043 // The ipmi standard dictates we enable the watchdog during reset
44 wd_service.resetTimeRemaining(true);
William A. Kennington III52575252018-02-09 15:54:56 -080045 return IPMI_CC_OK;
Patrick Venture5e6ac712017-10-25 12:16:19 -070046 }
William A. Kennington III021b4c12018-05-10 11:12:51 -070047 catch (const InternalFailure& e)
48 {
49 report<InternalFailure>();
50 return IPMI_CC_UNSPECIFIED_ERROR;
51 }
William A. Kennington III52575252018-02-09 15:54:56 -080052 catch (const std::exception& e)
53 {
54 const std::string e_str = std::string("wd_reset: ") + e.what();
55 log<level::ERR>(e_str.c_str());
William A. Kennington III021b4c12018-05-10 11:12:51 -070056 report<InternalFailure>();
William A. Kennington III52575252018-02-09 15:54:56 -080057 return IPMI_CC_UNSPECIFIED_ERROR;
William A. Kennington III5325f2c2018-01-08 15:17:09 -080058 }
William A. Kennington III52575252018-02-09 15:54:56 -080059 catch (...)
60 {
61 log<level::ERR>("wd_reset: Unknown Error");
William A. Kennington III021b4c12018-05-10 11:12:51 -070062 report<InternalFailure>();
William A. Kennington III52575252018-02-09 15:54:56 -080063 return IPMI_CC_UNSPECIFIED_ERROR;
William A. Kennington III5325f2c2018-01-08 15:17:09 -080064 }
Patrick Venture5e6ac712017-10-25 12:16:19 -070065}
William A. Kennington III61d5f7b2018-02-09 15:23:53 -080066
William A. Kennington III52575252018-02-09 15:54:56 -080067static constexpr uint8_t wd_dont_stop = 0x1 << 6;
68static constexpr uint8_t wd_timeout_action_mask = 0x3;
69
70enum class IpmiAction : uint8_t {
71 None = 0x0,
72 HardReset = 0x1,
73 PowerOff = 0x2,
74 PowerCycle = 0x3,
75};
76
William A. Kennington IIIb638de22018-02-09 16:12:53 -080077/** @brief Converts an IPMI Watchdog Action to DBUS defined action
78 * @param[in] ipmi_action The IPMI Watchdog Action
79 * @return The Watchdog Action that the ipmi_action maps to
80 */
81WatchdogService::Action ipmiActionToWdAction(IpmiAction ipmi_action)
82{
83 switch(ipmi_action)
84 {
85 case IpmiAction::None:
86 {
87 return WatchdogService::Action::None;
88 }
89 case IpmiAction::HardReset:
90 {
91 return WatchdogService::Action::HardReset;
92 }
93 case IpmiAction::PowerOff:
94 {
95 return WatchdogService::Action::PowerOff;
96 }
97 case IpmiAction::PowerCycle:
98 {
99 return WatchdogService::Action::PowerCycle;
100 }
101 default:
102 {
103 throw std::domain_error("IPMI Action is invalid");
104 }
105 }
106}
107
William A. Kennington III52575252018-02-09 15:54:56 -0800108struct wd_set_req {
109 uint8_t timer_use;
110 uint8_t timer_action;
111 uint8_t pretimeout; // (seconds)
112 uint8_t expire_flags;
113 uint16_t initial_countdown; // Little Endian (deciseconds)
114} __attribute__ ((packed));
115static_assert(sizeof(wd_set_req) == 6, "wd_set_req has invalid size.");
116static_assert(sizeof(wd_set_req) <= MAX_IPMI_BUFFER,
117 "wd_get_res can't fit in request buffer.");
118
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800119ipmi_ret_t ipmi_app_watchdog_set(
120 ipmi_netfn_t netfn,
121 ipmi_cmd_t cmd,
122 ipmi_request_t request,
123 ipmi_response_t response,
124 ipmi_data_len_t data_len,
125 ipmi_context_t context)
126{
William A. Kennington III52575252018-02-09 15:54:56 -0800127 // Extract the request data
128 if (*data_len < sizeof(wd_set_req))
129 {
130 *data_len = 0;
131 return IPMI_CC_REQ_DATA_LEN_INVALID;
132 }
133 wd_set_req req;
134 memcpy(&req, request, sizeof(req));
135 req.initial_countdown = le16toh(req.initial_countdown);
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800136 *data_len = 0;
137
William A. Kennington III52575252018-02-09 15:54:56 -0800138 try
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800139 {
William A. Kennington III52575252018-02-09 15:54:56 -0800140 WatchdogService wd_service;
141 // Stop the timer if the don't stop bit is not set
142 if (!(req.timer_use & wd_dont_stop))
143 {
144 wd_service.setEnabled(false);
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800145 }
146
William A. Kennington III52575252018-02-09 15:54:56 -0800147 // Set the action based on the request
William A. Kennington III52575252018-02-09 15:54:56 -0800148 const auto ipmi_action = static_cast<IpmiAction>(
149 req.timer_action & wd_timeout_action_mask);
William A. Kennington IIIb638de22018-02-09 16:12:53 -0800150 wd_service.setExpireAction(ipmiActionToWdAction(ipmi_action));
William A. Kennington III52575252018-02-09 15:54:56 -0800151
152 // Set the new interval and the time remaining deci -> mill seconds
153 const uint64_t interval = req.initial_countdown * 100;
154 wd_service.setInterval(interval);
155 wd_service.setTimeRemaining(interval);
156
William A. Kennington IIIde14a022018-02-09 16:11:18 -0800157 // Mark as initialized so that future resets behave correctly
158 wd_service.setInitialized(true);
159
William A. Kennington III52575252018-02-09 15:54:56 -0800160 return IPMI_CC_OK;
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800161 }
William A. Kennington III52575252018-02-09 15:54:56 -0800162 catch (const std::domain_error &)
163 {
164 return IPMI_CC_INVALID_FIELD_REQUEST;
165 }
William A. Kennington III021b4c12018-05-10 11:12:51 -0700166 catch (const InternalFailure& e)
167 {
168 report<InternalFailure>();
169 return IPMI_CC_UNSPECIFIED_ERROR;
170 }
William A. Kennington III52575252018-02-09 15:54:56 -0800171 catch (const std::exception& e)
172 {
173 const std::string e_str = std::string("wd_set: ") + e.what();
174 log<level::ERR>(e_str.c_str());
William A. Kennington III021b4c12018-05-10 11:12:51 -0700175 report<InternalFailure>();
William A. Kennington III52575252018-02-09 15:54:56 -0800176 return IPMI_CC_UNSPECIFIED_ERROR;
177 }
178 catch (...)
179 {
180 log<level::ERR>("wd_set: Unknown Error");
William A. Kennington III021b4c12018-05-10 11:12:51 -0700181 report<InternalFailure>();
William A. Kennington III52575252018-02-09 15:54:56 -0800182 return IPMI_CC_UNSPECIFIED_ERROR;
183 }
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800184}
William A. Kennington III73f44512018-02-09 15:28:46 -0800185
186/** @brief Converts a DBUS Watchdog Action to IPMI defined action
187 * @param[in] wd_action The DBUS Watchdog Action
188 * @return The IpmiAction that the wd_action maps to
189 */
190IpmiAction wdActionToIpmiAction(WatchdogService::Action wd_action)
191{
192 switch(wd_action)
193 {
194 case WatchdogService::Action::None:
195 {
196 return IpmiAction::None;
197 }
198 case WatchdogService::Action::HardReset:
199 {
200 return IpmiAction::HardReset;
201 }
202 case WatchdogService::Action::PowerOff:
203 {
204 return IpmiAction::PowerOff;
205 }
206 case WatchdogService::Action::PowerCycle:
207 {
208 return IpmiAction::PowerCycle;
209 }
210 default:
211 {
212 // We have no method via IPMI to signal that the action is unknown
213 // or unmappable in some way.
214 // Just ignore the error and return NONE so the host can reconcile.
215 return IpmiAction::None;
216 }
217 }
218}
219
220struct wd_get_res {
221 uint8_t timer_use;
222 uint8_t timer_action;
223 uint8_t pretimeout;
224 uint8_t expire_flags;
225 uint16_t initial_countdown; // Little Endian (deciseconds)
226 uint16_t present_countdown; // Little Endian (deciseconds)
227} __attribute__ ((packed));
228static_assert(sizeof(wd_get_res) == 8, "wd_get_res has invalid size.");
229static_assert(sizeof(wd_get_res) <= MAX_IPMI_BUFFER,
230 "wd_get_res can't fit in response buffer.");
231
232static constexpr uint8_t wd_dont_log = 0x1 << 7;
233static constexpr uint8_t wd_running = 0x1 << 6;
234
235ipmi_ret_t ipmi_app_watchdog_get(
236 ipmi_netfn_t netfn,
237 ipmi_cmd_t cmd,
238 ipmi_request_t request,
239 ipmi_response_t response,
240 ipmi_data_len_t data_len,
241 ipmi_context_t context)
242{
243 // Assume we will fail and send no data outside the return code
244 *data_len = 0;
245
246 try
247 {
248 WatchdogService wd_service;
249 WatchdogService::Properties wd_prop = wd_service.getProperties();
250
251 // Build and return the response
252 wd_get_res res;
253 res.timer_use = wd_dont_log;
254 res.timer_action = static_cast<uint8_t>(
255 wdActionToIpmiAction(wd_prop.expireAction));
256 if (wd_prop.enabled)
257 {
258 res.timer_use |= wd_running;
259 }
260 // TODO: Do something about having pretimeout support
261 res.pretimeout = 0;
262 res.expire_flags = 0;
263 // Interval and timeRemaining need converted from milli -> deci seconds
264 res.initial_countdown = htole16(wd_prop.interval / 100);
265 res.present_countdown = htole16(wd_prop.timeRemaining / 100);
266
267 memcpy(response, &res, sizeof(res));
268 *data_len = sizeof(res);
269 return IPMI_CC_OK;
270 }
William A. Kennington III021b4c12018-05-10 11:12:51 -0700271 catch (const InternalFailure& e)
272 {
273 report<InternalFailure>();
274 return IPMI_CC_UNSPECIFIED_ERROR;
275 }
William A. Kennington III73f44512018-02-09 15:28:46 -0800276 catch (const std::exception& e)
277 {
278 const std::string e_str = std::string("wd_get: ") + e.what();
279 log<level::ERR>(e_str.c_str());
William A. Kennington III021b4c12018-05-10 11:12:51 -0700280 report<InternalFailure>();
William A. Kennington III73f44512018-02-09 15:28:46 -0800281 return IPMI_CC_UNSPECIFIED_ERROR;
282 }
283 catch (...)
284 {
285 log<level::ERR>("wd_get: Unknown Error");
William A. Kennington III021b4c12018-05-10 11:12:51 -0700286 report<InternalFailure>();
William A. Kennington III73f44512018-02-09 15:28:46 -0800287 return IPMI_CC_UNSPECIFIED_ERROR;
288 }
289}