blob: f28ff8070300461787f9d1adb9abb913778e89ba [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
69struct wd_set_req {
70 uint8_t timer_use;
71 uint8_t timer_action;
72 uint8_t pretimeout; // (seconds)
73 uint8_t expire_flags;
74 uint16_t initial_countdown; // Little Endian (deciseconds)
75} __attribute__ ((packed));
76static_assert(sizeof(wd_set_req) == 6, "wd_set_req has invalid size.");
77static_assert(sizeof(wd_set_req) <= MAX_IPMI_BUFFER,
78 "wd_get_res can't fit in request buffer.");
79
William A. Kennington III61d5f7b2018-02-09 15:23:53 -080080ipmi_ret_t ipmi_app_watchdog_set(
81 ipmi_netfn_t netfn,
82 ipmi_cmd_t cmd,
83 ipmi_request_t request,
84 ipmi_response_t response,
85 ipmi_data_len_t data_len,
86 ipmi_context_t context)
87{
William A. Kennington III52575252018-02-09 15:54:56 -080088 // Extract the request data
89 if (*data_len < sizeof(wd_set_req))
90 {
91 *data_len = 0;
92 return IPMI_CC_REQ_DATA_LEN_INVALID;
93 }
94 wd_set_req req;
95 memcpy(&req, request, sizeof(req));
96 req.initial_countdown = le16toh(req.initial_countdown);
William A. Kennington III61d5f7b2018-02-09 15:23:53 -080097 *data_len = 0;
98
William A. Kennington III52575252018-02-09 15:54:56 -080099 try
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800100 {
William A. Kennington III52575252018-02-09 15:54:56 -0800101 WatchdogService wd_service;
102 // Stop the timer if the don't stop bit is not set
103 if (!(req.timer_use & wd_dont_stop))
104 {
105 wd_service.setEnabled(false);
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800106 }
107
William A. Kennington III52575252018-02-09 15:54:56 -0800108 // Set the action based on the request
109 // Unfortunately we only really support enable or disable
110 // and don't actually support a real action. Until we have proper
111 // action support just map NONE as a disable action.
112 const auto ipmi_action = static_cast<IpmiAction>(
113 req.timer_action & wd_timeout_action_mask);
114 if (ipmi_action == IpmiAction::None)
115 {
116 wd_service.setEnabled(false);
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800117 }
William A. Kennington III52575252018-02-09 15:54:56 -0800118
119 // Set the new interval and the time remaining deci -> mill seconds
120 const uint64_t interval = req.initial_countdown * 100;
121 wd_service.setInterval(interval);
122 wd_service.setTimeRemaining(interval);
123
William A. Kennington IIIde14a022018-02-09 16:11:18 -0800124 // Mark as initialized so that future resets behave correctly
125 wd_service.setInitialized(true);
126
William A. Kennington III52575252018-02-09 15:54:56 -0800127 return IPMI_CC_OK;
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800128 }
William A. Kennington III52575252018-02-09 15:54:56 -0800129 catch (const std::domain_error &)
130 {
131 return IPMI_CC_INVALID_FIELD_REQUEST;
132 }
133 catch (const std::exception& e)
134 {
135 const std::string e_str = std::string("wd_set: ") + e.what();
136 log<level::ERR>(e_str.c_str());
137 return IPMI_CC_UNSPECIFIED_ERROR;
138 }
139 catch (...)
140 {
141 log<level::ERR>("wd_set: Unknown Error");
142 return IPMI_CC_UNSPECIFIED_ERROR;
143 }
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800144}