blob: da8ccf3bf4efecbad105a9a6b928543d2117c8c9 [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;
William A. Kennington III52575252018-02-09 15:54:56 -080034
William A. Kennington IIIde14a022018-02-09 16:11:18 -080035 // Notify the caller if we haven't initialized our timer yet
36 // so it can configure actions and timeouts
William A. Kennington III2ecf5122018-04-27 14:31:51 -070037 if (!wd_service.getInitialized())
William A. Kennington IIIde14a022018-02-09 16:11:18 -080038 {
39 return IPMI_WDOG_CC_NOT_INIT;
40 }
41
William A. Kennington III4b017a92018-04-27 14:31:08 -070042 // The ipmi standard dictates we enable the watchdog during reset
43 wd_service.resetTimeRemaining(true);
William A. Kennington III52575252018-02-09 15:54:56 -080044 return IPMI_CC_OK;
Patrick Venture5e6ac712017-10-25 12:16:19 -070045 }
William A. Kennington III021b4c12018-05-10 11:12:51 -070046 catch (const InternalFailure& e)
47 {
48 report<InternalFailure>();
49 return IPMI_CC_UNSPECIFIED_ERROR;
50 }
William A. Kennington III52575252018-02-09 15:54:56 -080051 catch (const std::exception& e)
52 {
53 const std::string e_str = std::string("wd_reset: ") + e.what();
54 log<level::ERR>(e_str.c_str());
William A. Kennington III021b4c12018-05-10 11:12:51 -070055 report<InternalFailure>();
William A. Kennington III52575252018-02-09 15:54:56 -080056 return IPMI_CC_UNSPECIFIED_ERROR;
William A. Kennington III5325f2c2018-01-08 15:17:09 -080057 }
William A. Kennington III52575252018-02-09 15:54:56 -080058 catch (...)
59 {
60 log<level::ERR>("wd_reset: Unknown Error");
William A. Kennington III021b4c12018-05-10 11:12:51 -070061 report<InternalFailure>();
William A. Kennington III52575252018-02-09 15:54:56 -080062 return IPMI_CC_UNSPECIFIED_ERROR;
William A. Kennington III5325f2c2018-01-08 15:17:09 -080063 }
Patrick Venture5e6ac712017-10-25 12:16:19 -070064}
William A. Kennington III61d5f7b2018-02-09 15:23:53 -080065
William A. Kennington III52575252018-02-09 15:54:56 -080066static constexpr uint8_t wd_dont_stop = 0x1 << 6;
67static constexpr uint8_t wd_timeout_action_mask = 0x3;
68
69enum class IpmiAction : uint8_t {
70 None = 0x0,
71 HardReset = 0x1,
72 PowerOff = 0x2,
73 PowerCycle = 0x3,
74};
75
William A. Kennington IIIb638de22018-02-09 16:12:53 -080076/** @brief Converts an IPMI Watchdog Action to DBUS defined action
77 * @param[in] ipmi_action The IPMI Watchdog Action
78 * @return The Watchdog Action that the ipmi_action maps to
79 */
80WatchdogService::Action ipmiActionToWdAction(IpmiAction ipmi_action)
81{
82 switch(ipmi_action)
83 {
84 case IpmiAction::None:
85 {
86 return WatchdogService::Action::None;
87 }
88 case IpmiAction::HardReset:
89 {
90 return WatchdogService::Action::HardReset;
91 }
92 case IpmiAction::PowerOff:
93 {
94 return WatchdogService::Action::PowerOff;
95 }
96 case IpmiAction::PowerCycle:
97 {
98 return WatchdogService::Action::PowerCycle;
99 }
100 default:
101 {
102 throw std::domain_error("IPMI Action is invalid");
103 }
104 }
105}
106
William A. Kennington III52575252018-02-09 15:54:56 -0800107struct wd_set_req {
108 uint8_t timer_use;
109 uint8_t timer_action;
110 uint8_t pretimeout; // (seconds)
111 uint8_t expire_flags;
112 uint16_t initial_countdown; // Little Endian (deciseconds)
113} __attribute__ ((packed));
114static_assert(sizeof(wd_set_req) == 6, "wd_set_req has invalid size.");
115static_assert(sizeof(wd_set_req) <= MAX_IPMI_BUFFER,
116 "wd_get_res can't fit in request buffer.");
117
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800118ipmi_ret_t ipmi_app_watchdog_set(
119 ipmi_netfn_t netfn,
120 ipmi_cmd_t cmd,
121 ipmi_request_t request,
122 ipmi_response_t response,
123 ipmi_data_len_t data_len,
124 ipmi_context_t context)
125{
William A. Kennington III52575252018-02-09 15:54:56 -0800126 // Extract the request data
127 if (*data_len < sizeof(wd_set_req))
128 {
129 *data_len = 0;
130 return IPMI_CC_REQ_DATA_LEN_INVALID;
131 }
132 wd_set_req req;
133 memcpy(&req, request, sizeof(req));
134 req.initial_countdown = le16toh(req.initial_countdown);
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800135 *data_len = 0;
136
William A. Kennington III52575252018-02-09 15:54:56 -0800137 try
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800138 {
William A. Kennington III52575252018-02-09 15:54:56 -0800139 WatchdogService wd_service;
140 // Stop the timer if the don't stop bit is not set
141 if (!(req.timer_use & wd_dont_stop))
142 {
143 wd_service.setEnabled(false);
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800144 }
145
William A. Kennington III52575252018-02-09 15:54:56 -0800146 // Set the action based on the request
William A. Kennington III52575252018-02-09 15:54:56 -0800147 const auto ipmi_action = static_cast<IpmiAction>(
148 req.timer_action & wd_timeout_action_mask);
William A. Kennington IIIb638de22018-02-09 16:12:53 -0800149 wd_service.setExpireAction(ipmiActionToWdAction(ipmi_action));
William A. Kennington III52575252018-02-09 15:54:56 -0800150
151 // Set the new interval and the time remaining deci -> mill seconds
152 const uint64_t interval = req.initial_countdown * 100;
153 wd_service.setInterval(interval);
154 wd_service.setTimeRemaining(interval);
155
William A. Kennington IIIde14a022018-02-09 16:11:18 -0800156 // Mark as initialized so that future resets behave correctly
157 wd_service.setInitialized(true);
158
William A. Kennington III52575252018-02-09 15:54:56 -0800159 return IPMI_CC_OK;
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800160 }
William A. Kennington III52575252018-02-09 15:54:56 -0800161 catch (const std::domain_error &)
162 {
163 return IPMI_CC_INVALID_FIELD_REQUEST;
164 }
William A. Kennington III021b4c12018-05-10 11:12:51 -0700165 catch (const InternalFailure& e)
166 {
167 report<InternalFailure>();
168 return IPMI_CC_UNSPECIFIED_ERROR;
169 }
William A. Kennington III52575252018-02-09 15:54:56 -0800170 catch (const std::exception& e)
171 {
172 const std::string e_str = std::string("wd_set: ") + e.what();
173 log<level::ERR>(e_str.c_str());
William A. Kennington III021b4c12018-05-10 11:12:51 -0700174 report<InternalFailure>();
William A. Kennington III52575252018-02-09 15:54:56 -0800175 return IPMI_CC_UNSPECIFIED_ERROR;
176 }
177 catch (...)
178 {
179 log<level::ERR>("wd_set: Unknown Error");
William A. Kennington III021b4c12018-05-10 11:12:51 -0700180 report<InternalFailure>();
William A. Kennington III52575252018-02-09 15:54:56 -0800181 return IPMI_CC_UNSPECIFIED_ERROR;
182 }
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800183}
William A. Kennington III73f44512018-02-09 15:28:46 -0800184
185/** @brief Converts a DBUS Watchdog Action to IPMI defined action
186 * @param[in] wd_action The DBUS Watchdog Action
187 * @return The IpmiAction that the wd_action maps to
188 */
189IpmiAction wdActionToIpmiAction(WatchdogService::Action wd_action)
190{
191 switch(wd_action)
192 {
193 case WatchdogService::Action::None:
194 {
195 return IpmiAction::None;
196 }
197 case WatchdogService::Action::HardReset:
198 {
199 return IpmiAction::HardReset;
200 }
201 case WatchdogService::Action::PowerOff:
202 {
203 return IpmiAction::PowerOff;
204 }
205 case WatchdogService::Action::PowerCycle:
206 {
207 return IpmiAction::PowerCycle;
208 }
209 default:
210 {
211 // We have no method via IPMI to signal that the action is unknown
212 // or unmappable in some way.
213 // Just ignore the error and return NONE so the host can reconcile.
214 return IpmiAction::None;
215 }
216 }
217}
218
219struct wd_get_res {
220 uint8_t timer_use;
221 uint8_t timer_action;
222 uint8_t pretimeout;
223 uint8_t expire_flags;
224 uint16_t initial_countdown; // Little Endian (deciseconds)
225 uint16_t present_countdown; // Little Endian (deciseconds)
226} __attribute__ ((packed));
227static_assert(sizeof(wd_get_res) == 8, "wd_get_res has invalid size.");
228static_assert(sizeof(wd_get_res) <= MAX_IPMI_BUFFER,
229 "wd_get_res can't fit in response buffer.");
230
231static constexpr uint8_t wd_dont_log = 0x1 << 7;
232static constexpr uint8_t wd_running = 0x1 << 6;
233
234ipmi_ret_t ipmi_app_watchdog_get(
235 ipmi_netfn_t netfn,
236 ipmi_cmd_t cmd,
237 ipmi_request_t request,
238 ipmi_response_t response,
239 ipmi_data_len_t data_len,
240 ipmi_context_t context)
241{
242 // Assume we will fail and send no data outside the return code
243 *data_len = 0;
244
245 try
246 {
247 WatchdogService wd_service;
248 WatchdogService::Properties wd_prop = wd_service.getProperties();
249
250 // Build and return the response
251 wd_get_res res;
252 res.timer_use = wd_dont_log;
253 res.timer_action = static_cast<uint8_t>(
254 wdActionToIpmiAction(wd_prop.expireAction));
255 if (wd_prop.enabled)
256 {
257 res.timer_use |= wd_running;
258 }
259 // TODO: Do something about having pretimeout support
260 res.pretimeout = 0;
261 res.expire_flags = 0;
262 // Interval and timeRemaining need converted from milli -> deci seconds
263 res.initial_countdown = htole16(wd_prop.interval / 100);
264 res.present_countdown = htole16(wd_prop.timeRemaining / 100);
265
266 memcpy(response, &res, sizeof(res));
267 *data_len = sizeof(res);
268 return IPMI_CC_OK;
269 }
William A. Kennington III021b4c12018-05-10 11:12:51 -0700270 catch (const InternalFailure& e)
271 {
272 report<InternalFailure>();
273 return IPMI_CC_UNSPECIFIED_ERROR;
274 }
William A. Kennington III73f44512018-02-09 15:28:46 -0800275 catch (const std::exception& e)
276 {
277 const std::string e_str = std::string("wd_get: ") + e.what();
278 log<level::ERR>(e_str.c_str());
William A. Kennington III021b4c12018-05-10 11:12:51 -0700279 report<InternalFailure>();
William A. Kennington III73f44512018-02-09 15:28:46 -0800280 return IPMI_CC_UNSPECIFIED_ERROR;
281 }
282 catch (...)
283 {
284 log<level::ERR>("wd_get: Unknown Error");
William A. Kennington III021b4c12018-05-10 11:12:51 -0700285 report<InternalFailure>();
William A. Kennington III73f44512018-02-09 15:28:46 -0800286 return IPMI_CC_UNSPECIFIED_ERROR;
287 }
288}