blob: c1f25ac1e5bed928ebcd3602a2ce22cf7886ba12 [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 III52575252018-02-09 15:54:56 -080043 // Reset the countdown to make sure we don't expire our timer
44 wd_service.setTimeRemaining(wd_prop.interval);
45
46 // The spec states that the timer is activated by reset
47 wd_service.setEnabled(true);
48
49 return IPMI_CC_OK;
Patrick Venture5e6ac712017-10-25 12:16:19 -070050 }
William A. Kennington III021b4c12018-05-10 11:12:51 -070051 catch (const InternalFailure& e)
52 {
53 report<InternalFailure>();
54 return IPMI_CC_UNSPECIFIED_ERROR;
55 }
William A. Kennington III52575252018-02-09 15:54:56 -080056 catch (const std::exception& e)
57 {
58 const std::string e_str = std::string("wd_reset: ") + e.what();
59 log<level::ERR>(e_str.c_str());
William A. Kennington III021b4c12018-05-10 11:12:51 -070060 report<InternalFailure>();
William A. Kennington III52575252018-02-09 15:54:56 -080061 return IPMI_CC_UNSPECIFIED_ERROR;
William A. Kennington III5325f2c2018-01-08 15:17:09 -080062 }
William A. Kennington III52575252018-02-09 15:54:56 -080063 catch (...)
64 {
65 log<level::ERR>("wd_reset: Unknown Error");
William A. Kennington III021b4c12018-05-10 11:12:51 -070066 report<InternalFailure>();
William A. Kennington III52575252018-02-09 15:54:56 -080067 return IPMI_CC_UNSPECIFIED_ERROR;
William A. Kennington III5325f2c2018-01-08 15:17:09 -080068 }
Patrick Venture5e6ac712017-10-25 12:16:19 -070069}
William A. Kennington III61d5f7b2018-02-09 15:23:53 -080070
William A. Kennington III52575252018-02-09 15:54:56 -080071static constexpr uint8_t wd_dont_stop = 0x1 << 6;
72static constexpr uint8_t wd_timeout_action_mask = 0x3;
73
74enum class IpmiAction : uint8_t {
75 None = 0x0,
76 HardReset = 0x1,
77 PowerOff = 0x2,
78 PowerCycle = 0x3,
79};
80
William A. Kennington IIIb638de22018-02-09 16:12:53 -080081/** @brief Converts an IPMI Watchdog Action to DBUS defined action
82 * @param[in] ipmi_action The IPMI Watchdog Action
83 * @return The Watchdog Action that the ipmi_action maps to
84 */
85WatchdogService::Action ipmiActionToWdAction(IpmiAction ipmi_action)
86{
87 switch(ipmi_action)
88 {
89 case IpmiAction::None:
90 {
91 return WatchdogService::Action::None;
92 }
93 case IpmiAction::HardReset:
94 {
95 return WatchdogService::Action::HardReset;
96 }
97 case IpmiAction::PowerOff:
98 {
99 return WatchdogService::Action::PowerOff;
100 }
101 case IpmiAction::PowerCycle:
102 {
103 return WatchdogService::Action::PowerCycle;
104 }
105 default:
106 {
107 throw std::domain_error("IPMI Action is invalid");
108 }
109 }
110}
111
William A. Kennington III52575252018-02-09 15:54:56 -0800112struct wd_set_req {
113 uint8_t timer_use;
114 uint8_t timer_action;
115 uint8_t pretimeout; // (seconds)
116 uint8_t expire_flags;
117 uint16_t initial_countdown; // Little Endian (deciseconds)
118} __attribute__ ((packed));
119static_assert(sizeof(wd_set_req) == 6, "wd_set_req has invalid size.");
120static_assert(sizeof(wd_set_req) <= MAX_IPMI_BUFFER,
121 "wd_get_res can't fit in request buffer.");
122
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800123ipmi_ret_t ipmi_app_watchdog_set(
124 ipmi_netfn_t netfn,
125 ipmi_cmd_t cmd,
126 ipmi_request_t request,
127 ipmi_response_t response,
128 ipmi_data_len_t data_len,
129 ipmi_context_t context)
130{
William A. Kennington III52575252018-02-09 15:54:56 -0800131 // Extract the request data
132 if (*data_len < sizeof(wd_set_req))
133 {
134 *data_len = 0;
135 return IPMI_CC_REQ_DATA_LEN_INVALID;
136 }
137 wd_set_req req;
138 memcpy(&req, request, sizeof(req));
139 req.initial_countdown = le16toh(req.initial_countdown);
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800140 *data_len = 0;
141
William A. Kennington III52575252018-02-09 15:54:56 -0800142 try
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800143 {
William A. Kennington III52575252018-02-09 15:54:56 -0800144 WatchdogService wd_service;
145 // Stop the timer if the don't stop bit is not set
146 if (!(req.timer_use & wd_dont_stop))
147 {
148 wd_service.setEnabled(false);
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800149 }
150
William A. Kennington III52575252018-02-09 15:54:56 -0800151 // Set the action based on the request
William A. Kennington III52575252018-02-09 15:54:56 -0800152 const auto ipmi_action = static_cast<IpmiAction>(
153 req.timer_action & wd_timeout_action_mask);
William A. Kennington IIIb638de22018-02-09 16:12:53 -0800154 wd_service.setExpireAction(ipmiActionToWdAction(ipmi_action));
William A. Kennington III52575252018-02-09 15:54:56 -0800155
156 // Set the new interval and the time remaining deci -> mill seconds
157 const uint64_t interval = req.initial_countdown * 100;
158 wd_service.setInterval(interval);
159 wd_service.setTimeRemaining(interval);
160
William A. Kennington IIIde14a022018-02-09 16:11:18 -0800161 // Mark as initialized so that future resets behave correctly
162 wd_service.setInitialized(true);
163
William A. Kennington III52575252018-02-09 15:54:56 -0800164 return IPMI_CC_OK;
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800165 }
William A. Kennington III52575252018-02-09 15:54:56 -0800166 catch (const std::domain_error &)
167 {
168 return IPMI_CC_INVALID_FIELD_REQUEST;
169 }
William A. Kennington III021b4c12018-05-10 11:12:51 -0700170 catch (const InternalFailure& e)
171 {
172 report<InternalFailure>();
173 return IPMI_CC_UNSPECIFIED_ERROR;
174 }
William A. Kennington III52575252018-02-09 15:54:56 -0800175 catch (const std::exception& e)
176 {
177 const std::string e_str = std::string("wd_set: ") + e.what();
178 log<level::ERR>(e_str.c_str());
William A. Kennington III021b4c12018-05-10 11:12:51 -0700179 report<InternalFailure>();
William A. Kennington III52575252018-02-09 15:54:56 -0800180 return IPMI_CC_UNSPECIFIED_ERROR;
181 }
182 catch (...)
183 {
184 log<level::ERR>("wd_set: Unknown Error");
William A. Kennington III021b4c12018-05-10 11:12:51 -0700185 report<InternalFailure>();
William A. Kennington III52575252018-02-09 15:54:56 -0800186 return IPMI_CC_UNSPECIFIED_ERROR;
187 }
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800188}
William A. Kennington III73f44512018-02-09 15:28:46 -0800189
190/** @brief Converts a DBUS Watchdog Action to IPMI defined action
191 * @param[in] wd_action The DBUS Watchdog Action
192 * @return The IpmiAction that the wd_action maps to
193 */
194IpmiAction wdActionToIpmiAction(WatchdogService::Action wd_action)
195{
196 switch(wd_action)
197 {
198 case WatchdogService::Action::None:
199 {
200 return IpmiAction::None;
201 }
202 case WatchdogService::Action::HardReset:
203 {
204 return IpmiAction::HardReset;
205 }
206 case WatchdogService::Action::PowerOff:
207 {
208 return IpmiAction::PowerOff;
209 }
210 case WatchdogService::Action::PowerCycle:
211 {
212 return IpmiAction::PowerCycle;
213 }
214 default:
215 {
216 // We have no method via IPMI to signal that the action is unknown
217 // or unmappable in some way.
218 // Just ignore the error and return NONE so the host can reconcile.
219 return IpmiAction::None;
220 }
221 }
222}
223
224struct wd_get_res {
225 uint8_t timer_use;
226 uint8_t timer_action;
227 uint8_t pretimeout;
228 uint8_t expire_flags;
229 uint16_t initial_countdown; // Little Endian (deciseconds)
230 uint16_t present_countdown; // Little Endian (deciseconds)
231} __attribute__ ((packed));
232static_assert(sizeof(wd_get_res) == 8, "wd_get_res has invalid size.");
233static_assert(sizeof(wd_get_res) <= MAX_IPMI_BUFFER,
234 "wd_get_res can't fit in response buffer.");
235
236static constexpr uint8_t wd_dont_log = 0x1 << 7;
237static constexpr uint8_t wd_running = 0x1 << 6;
238
239ipmi_ret_t ipmi_app_watchdog_get(
240 ipmi_netfn_t netfn,
241 ipmi_cmd_t cmd,
242 ipmi_request_t request,
243 ipmi_response_t response,
244 ipmi_data_len_t data_len,
245 ipmi_context_t context)
246{
247 // Assume we will fail and send no data outside the return code
248 *data_len = 0;
249
250 try
251 {
252 WatchdogService wd_service;
253 WatchdogService::Properties wd_prop = wd_service.getProperties();
254
255 // Build and return the response
256 wd_get_res res;
257 res.timer_use = wd_dont_log;
258 res.timer_action = static_cast<uint8_t>(
259 wdActionToIpmiAction(wd_prop.expireAction));
260 if (wd_prop.enabled)
261 {
262 res.timer_use |= wd_running;
263 }
264 // TODO: Do something about having pretimeout support
265 res.pretimeout = 0;
266 res.expire_flags = 0;
267 // Interval and timeRemaining need converted from milli -> deci seconds
268 res.initial_countdown = htole16(wd_prop.interval / 100);
269 res.present_countdown = htole16(wd_prop.timeRemaining / 100);
270
271 memcpy(response, &res, sizeof(res));
272 *data_len = sizeof(res);
273 return IPMI_CC_OK;
274 }
William A. Kennington III021b4c12018-05-10 11:12:51 -0700275 catch (const InternalFailure& e)
276 {
277 report<InternalFailure>();
278 return IPMI_CC_UNSPECIFIED_ERROR;
279 }
William A. Kennington III73f44512018-02-09 15:28:46 -0800280 catch (const std::exception& e)
281 {
282 const std::string e_str = std::string("wd_get: ") + e.what();
283 log<level::ERR>(e_str.c_str());
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 catch (...)
288 {
289 log<level::ERR>("wd_get: Unknown Error");
William A. Kennington III021b4c12018-05-10 11:12:51 -0700290 report<InternalFailure>();
William A. Kennington III73f44512018-02-09 15:28:46 -0800291 return IPMI_CC_UNSPECIFIED_ERROR;
292 }
293}