blob: 7b1d987963a1061fcf2bc35ae1fa2b1cd45b819d [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>
5#include <phosphor-logging/log.hpp>
6#include <string>
Patrick Venture894571d2017-11-09 14:46:54 -08007
William A. Kennington III52575252018-02-09 15:54:56 -08008#include "watchdog_service.hpp"
9#include "host-ipmid/ipmid-api.h"
10#include "ipmid.hpp"
Patrick Venture5e6ac712017-10-25 12:16:19 -070011
William A. Kennington III52575252018-02-09 15:54:56 -080012using phosphor::logging::level;
13using phosphor::logging::log;
Patrick Venture5e6ac712017-10-25 12:16:19 -070014
William A. Kennington III61d5f7b2018-02-09 15:23:53 -080015ipmi_ret_t ipmi_app_watchdog_reset(
Patrick Venture894571d2017-11-09 14:46:54 -080016 ipmi_netfn_t netfn,
17 ipmi_cmd_t cmd,
18 ipmi_request_t request,
19 ipmi_response_t response,
20 ipmi_data_len_t data_len,
21 ipmi_context_t context)
Patrick Venture5e6ac712017-10-25 12:16:19 -070022{
William A. Kennington III52575252018-02-09 15:54:56 -080023 // We never return data with this command so immediately get rid of it
Patrick Venture5e6ac712017-10-25 12:16:19 -070024 *data_len = 0;
25
William A. Kennington III52575252018-02-09 15:54:56 -080026 try
27 {
28 WatchdogService wd_service;
29 WatchdogService::Properties wd_prop = wd_service.getProperties();
30
William A. Kennington IIIde14a022018-02-09 16:11:18 -080031 // Notify the caller if we haven't initialized our timer yet
32 // so it can configure actions and timeouts
33 if (!wd_prop.initialized)
34 {
35 return IPMI_WDOG_CC_NOT_INIT;
36 }
37
William A. Kennington III52575252018-02-09 15:54:56 -080038 // Reset the countdown to make sure we don't expire our timer
39 wd_service.setTimeRemaining(wd_prop.interval);
40
41 // The spec states that the timer is activated by reset
42 wd_service.setEnabled(true);
43
44 return IPMI_CC_OK;
Patrick Venture5e6ac712017-10-25 12:16:19 -070045 }
William A. Kennington III52575252018-02-09 15:54:56 -080046 catch (const std::exception& e)
47 {
48 const std::string e_str = std::string("wd_reset: ") + e.what();
49 log<level::ERR>(e_str.c_str());
50 return IPMI_CC_UNSPECIFIED_ERROR;
William A. Kennington III5325f2c2018-01-08 15:17:09 -080051 }
William A. Kennington III52575252018-02-09 15:54:56 -080052 catch (...)
53 {
54 log<level::ERR>("wd_reset: Unknown Error");
55 return IPMI_CC_UNSPECIFIED_ERROR;
William A. Kennington III5325f2c2018-01-08 15:17:09 -080056 }
Patrick Venture5e6ac712017-10-25 12:16:19 -070057}
William A. Kennington III61d5f7b2018-02-09 15:23:53 -080058
William A. Kennington III52575252018-02-09 15:54:56 -080059static constexpr uint8_t wd_dont_stop = 0x1 << 6;
60static constexpr uint8_t wd_timeout_action_mask = 0x3;
61
62enum class IpmiAction : uint8_t {
63 None = 0x0,
64 HardReset = 0x1,
65 PowerOff = 0x2,
66 PowerCycle = 0x3,
67};
68
William A. Kennington IIIb638de22018-02-09 16:12:53 -080069/** @brief Converts an IPMI Watchdog Action to DBUS defined action
70 * @param[in] ipmi_action The IPMI Watchdog Action
71 * @return The Watchdog Action that the ipmi_action maps to
72 */
73WatchdogService::Action ipmiActionToWdAction(IpmiAction ipmi_action)
74{
75 switch(ipmi_action)
76 {
77 case IpmiAction::None:
78 {
79 return WatchdogService::Action::None;
80 }
81 case IpmiAction::HardReset:
82 {
83 return WatchdogService::Action::HardReset;
84 }
85 case IpmiAction::PowerOff:
86 {
87 return WatchdogService::Action::PowerOff;
88 }
89 case IpmiAction::PowerCycle:
90 {
91 return WatchdogService::Action::PowerCycle;
92 }
93 default:
94 {
95 throw std::domain_error("IPMI Action is invalid");
96 }
97 }
98}
99
William A. Kennington III52575252018-02-09 15:54:56 -0800100struct wd_set_req {
101 uint8_t timer_use;
102 uint8_t timer_action;
103 uint8_t pretimeout; // (seconds)
104 uint8_t expire_flags;
105 uint16_t initial_countdown; // Little Endian (deciseconds)
106} __attribute__ ((packed));
107static_assert(sizeof(wd_set_req) == 6, "wd_set_req has invalid size.");
108static_assert(sizeof(wd_set_req) <= MAX_IPMI_BUFFER,
109 "wd_get_res can't fit in request buffer.");
110
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800111ipmi_ret_t ipmi_app_watchdog_set(
112 ipmi_netfn_t netfn,
113 ipmi_cmd_t cmd,
114 ipmi_request_t request,
115 ipmi_response_t response,
116 ipmi_data_len_t data_len,
117 ipmi_context_t context)
118{
William A. Kennington III52575252018-02-09 15:54:56 -0800119 // Extract the request data
120 if (*data_len < sizeof(wd_set_req))
121 {
122 *data_len = 0;
123 return IPMI_CC_REQ_DATA_LEN_INVALID;
124 }
125 wd_set_req req;
126 memcpy(&req, request, sizeof(req));
127 req.initial_countdown = le16toh(req.initial_countdown);
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800128 *data_len = 0;
129
William A. Kennington III52575252018-02-09 15:54:56 -0800130 try
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800131 {
William A. Kennington III52575252018-02-09 15:54:56 -0800132 WatchdogService wd_service;
133 // Stop the timer if the don't stop bit is not set
134 if (!(req.timer_use & wd_dont_stop))
135 {
136 wd_service.setEnabled(false);
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800137 }
138
William A. Kennington III52575252018-02-09 15:54:56 -0800139 // Set the action based on the request
William A. Kennington III52575252018-02-09 15:54:56 -0800140 const auto ipmi_action = static_cast<IpmiAction>(
141 req.timer_action & wd_timeout_action_mask);
William A. Kennington IIIb638de22018-02-09 16:12:53 -0800142 wd_service.setExpireAction(ipmiActionToWdAction(ipmi_action));
William A. Kennington III52575252018-02-09 15:54:56 -0800143
144 // Set the new interval and the time remaining deci -> mill seconds
145 const uint64_t interval = req.initial_countdown * 100;
146 wd_service.setInterval(interval);
147 wd_service.setTimeRemaining(interval);
148
William A. Kennington IIIde14a022018-02-09 16:11:18 -0800149 // Mark as initialized so that future resets behave correctly
150 wd_service.setInitialized(true);
151
William A. Kennington III52575252018-02-09 15:54:56 -0800152 return IPMI_CC_OK;
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800153 }
William A. Kennington III52575252018-02-09 15:54:56 -0800154 catch (const std::domain_error &)
155 {
156 return IPMI_CC_INVALID_FIELD_REQUEST;
157 }
158 catch (const std::exception& e)
159 {
160 const std::string e_str = std::string("wd_set: ") + e.what();
161 log<level::ERR>(e_str.c_str());
162 return IPMI_CC_UNSPECIFIED_ERROR;
163 }
164 catch (...)
165 {
166 log<level::ERR>("wd_set: Unknown Error");
167 return IPMI_CC_UNSPECIFIED_ERROR;
168 }
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800169}
William A. Kennington III73f44512018-02-09 15:28:46 -0800170
171/** @brief Converts a DBUS Watchdog Action to IPMI defined action
172 * @param[in] wd_action The DBUS Watchdog Action
173 * @return The IpmiAction that the wd_action maps to
174 */
175IpmiAction wdActionToIpmiAction(WatchdogService::Action wd_action)
176{
177 switch(wd_action)
178 {
179 case WatchdogService::Action::None:
180 {
181 return IpmiAction::None;
182 }
183 case WatchdogService::Action::HardReset:
184 {
185 return IpmiAction::HardReset;
186 }
187 case WatchdogService::Action::PowerOff:
188 {
189 return IpmiAction::PowerOff;
190 }
191 case WatchdogService::Action::PowerCycle:
192 {
193 return IpmiAction::PowerCycle;
194 }
195 default:
196 {
197 // We have no method via IPMI to signal that the action is unknown
198 // or unmappable in some way.
199 // Just ignore the error and return NONE so the host can reconcile.
200 return IpmiAction::None;
201 }
202 }
203}
204
205struct wd_get_res {
206 uint8_t timer_use;
207 uint8_t timer_action;
208 uint8_t pretimeout;
209 uint8_t expire_flags;
210 uint16_t initial_countdown; // Little Endian (deciseconds)
211 uint16_t present_countdown; // Little Endian (deciseconds)
212} __attribute__ ((packed));
213static_assert(sizeof(wd_get_res) == 8, "wd_get_res has invalid size.");
214static_assert(sizeof(wd_get_res) <= MAX_IPMI_BUFFER,
215 "wd_get_res can't fit in response buffer.");
216
217static constexpr uint8_t wd_dont_log = 0x1 << 7;
218static constexpr uint8_t wd_running = 0x1 << 6;
219
220ipmi_ret_t ipmi_app_watchdog_get(
221 ipmi_netfn_t netfn,
222 ipmi_cmd_t cmd,
223 ipmi_request_t request,
224 ipmi_response_t response,
225 ipmi_data_len_t data_len,
226 ipmi_context_t context)
227{
228 // Assume we will fail and send no data outside the return code
229 *data_len = 0;
230
231 try
232 {
233 WatchdogService wd_service;
234 WatchdogService::Properties wd_prop = wd_service.getProperties();
235
236 // Build and return the response
237 wd_get_res res;
238 res.timer_use = wd_dont_log;
239 res.timer_action = static_cast<uint8_t>(
240 wdActionToIpmiAction(wd_prop.expireAction));
241 if (wd_prop.enabled)
242 {
243 res.timer_use |= wd_running;
244 }
245 // TODO: Do something about having pretimeout support
246 res.pretimeout = 0;
247 res.expire_flags = 0;
248 // Interval and timeRemaining need converted from milli -> deci seconds
249 res.initial_countdown = htole16(wd_prop.interval / 100);
250 res.present_countdown = htole16(wd_prop.timeRemaining / 100);
251
252 memcpy(response, &res, sizeof(res));
253 *data_len = sizeof(res);
254 return IPMI_CC_OK;
255 }
256 catch (const std::exception& e)
257 {
258 const std::string e_str = std::string("wd_get: ") + e.what();
259 log<level::ERR>(e_str.c_str());
260 return IPMI_CC_UNSPECIFIED_ERROR;
261 }
262 catch (...)
263 {
264 log<level::ERR>("wd_get: Unknown Error");
265 return IPMI_CC_UNSPECIFIED_ERROR;
266 }
267}