blob: ad167dfe6c71cd2d7c63e46ab061c1d38dca03ec [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 III52575252018-02-09 15:54:56 -080017using phosphor::logging::level;
18using phosphor::logging::log;
William A. Kennington III021b4c12018-05-10 11:12:51 -070019using phosphor::logging::report;
20using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
Patrick Venture5e6ac712017-10-25 12:16:19 -070021
Patrick Venture0b02be92018-08-31 11:55:55 -070022ipmi_ret_t ipmi_app_watchdog_reset(ipmi_netfn_t netfn, 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
Patrick Venture0b02be92018-08-31 11:55:55 -070069enum class IpmiAction : uint8_t
70{
William A. Kennington III52575252018-02-09 15:54:56 -080071 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{
Patrick Venture0b02be92018-08-31 11:55:55 -070083 switch (ipmi_action)
William A. Kennington IIIb638de22018-02-09 16:12:53 -080084 {
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
Patrick Venture0b02be92018-08-31 11:55:55 -0700108struct wd_set_req
109{
William A. Kennington III52575252018-02-09 15:54:56 -0800110 uint8_t timer_use;
111 uint8_t timer_action;
Patrick Venture0b02be92018-08-31 11:55:55 -0700112 uint8_t pretimeout; // (seconds)
William A. Kennington III52575252018-02-09 15:54:56 -0800113 uint8_t expire_flags;
Patrick Venture0b02be92018-08-31 11:55:55 -0700114 uint16_t initial_countdown; // Little Endian (deciseconds)
115} __attribute__((packed));
William A. Kennington III52575252018-02-09 15:54:56 -0800116static_assert(sizeof(wd_set_req) == 6, "wd_set_req has invalid size.");
117static_assert(sizeof(wd_set_req) <= MAX_IPMI_BUFFER,
Patrick Venture0b02be92018-08-31 11:55:55 -0700118 "wd_get_res can't fit in request buffer.");
William A. Kennington III52575252018-02-09 15:54:56 -0800119
Patrick Venture0b02be92018-08-31 11:55:55 -0700120ipmi_ret_t ipmi_app_watchdog_set(ipmi_netfn_t netfn, 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)
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800125{
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
Patrick Venture0b02be92018-08-31 11:55:55 -0700147 const auto ipmi_action =
148 static_cast<IpmiAction>(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 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700161 catch (const std::domain_error&)
William A. Kennington III52575252018-02-09 15:54:56 -0800162 {
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{
Patrick Venture0b02be92018-08-31 11:55:55 -0700191 switch (wd_action)
William A. Kennington III73f44512018-02-09 15:28:46 -0800192 {
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
Patrick Venture0b02be92018-08-31 11:55:55 -0700219struct wd_get_res
220{
William A. Kennington III73f44512018-02-09 15:28:46 -0800221 uint8_t timer_use;
222 uint8_t timer_action;
223 uint8_t pretimeout;
224 uint8_t expire_flags;
Patrick Venture0b02be92018-08-31 11:55:55 -0700225 uint16_t initial_countdown; // Little Endian (deciseconds)
226 uint16_t present_countdown; // Little Endian (deciseconds)
227} __attribute__((packed));
William A. Kennington III73f44512018-02-09 15:28:46 -0800228static_assert(sizeof(wd_get_res) == 8, "wd_get_res has invalid size.");
229static_assert(sizeof(wd_get_res) <= MAX_IPMI_BUFFER,
Patrick Venture0b02be92018-08-31 11:55:55 -0700230 "wd_get_res can't fit in response buffer.");
William A. Kennington III73f44512018-02-09 15:28:46 -0800231
232static constexpr uint8_t wd_dont_log = 0x1 << 7;
233static constexpr uint8_t wd_running = 0x1 << 6;
234
Patrick Venture0b02be92018-08-31 11:55:55 -0700235ipmi_ret_t ipmi_app_watchdog_get(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
236 ipmi_request_t request,
237 ipmi_response_t response,
238 ipmi_data_len_t data_len,
239 ipmi_context_t context)
William A. Kennington III73f44512018-02-09 15:28:46 -0800240{
241 // Assume we will fail and send no data outside the return code
242 *data_len = 0;
243
244 try
245 {
246 WatchdogService wd_service;
247 WatchdogService::Properties wd_prop = wd_service.getProperties();
248
249 // Build and return the response
250 wd_get_res res;
251 res.timer_use = wd_dont_log;
Patrick Venture0b02be92018-08-31 11:55:55 -0700252 res.timer_action =
253 static_cast<uint8_t>(wdActionToIpmiAction(wd_prop.expireAction));
William A. Kennington III73f44512018-02-09 15:28:46 -0800254 if (wd_prop.enabled)
255 {
256 res.timer_use |= wd_running;
257 }
258 // TODO: Do something about having pretimeout support
259 res.pretimeout = 0;
260 res.expire_flags = 0;
261 // Interval and timeRemaining need converted from milli -> deci seconds
262 res.initial_countdown = htole16(wd_prop.interval / 100);
263 res.present_countdown = htole16(wd_prop.timeRemaining / 100);
264
265 memcpy(response, &res, sizeof(res));
266 *data_len = sizeof(res);
267 return IPMI_CC_OK;
268 }
William A. Kennington III021b4c12018-05-10 11:12:51 -0700269 catch (const InternalFailure& e)
270 {
271 report<InternalFailure>();
272 return IPMI_CC_UNSPECIFIED_ERROR;
273 }
William A. Kennington III73f44512018-02-09 15:28:46 -0800274 catch (const std::exception& e)
275 {
276 const std::string e_str = std::string("wd_get: ") + e.what();
277 log<level::ERR>(e_str.c_str());
William A. Kennington III021b4c12018-05-10 11:12:51 -0700278 report<InternalFailure>();
William A. Kennington III73f44512018-02-09 15:28:46 -0800279 return IPMI_CC_UNSPECIFIED_ERROR;
280 }
281 catch (...)
282 {
283 log<level::ERR>("wd_get: Unknown Error");
William A. Kennington III021b4c12018-05-10 11:12:51 -0700284 report<InternalFailure>();
William A. Kennington III73f44512018-02-09 15:28:46 -0800285 return IPMI_CC_UNSPECIFIED_ERROR;
286 }
287}