blob: d537683e5819a2718f67e0aef501c874cdea2439 [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
31 // Reset the countdown to make sure we don't expire our timer
32 wd_service.setTimeRemaining(wd_prop.interval);
33
34 // The spec states that the timer is activated by reset
35 wd_service.setEnabled(true);
36
37 return IPMI_CC_OK;
Patrick Venture5e6ac712017-10-25 12:16:19 -070038 }
William A. Kennington III52575252018-02-09 15:54:56 -080039 catch (const std::exception& e)
40 {
41 const std::string e_str = std::string("wd_reset: ") + e.what();
42 log<level::ERR>(e_str.c_str());
43 return IPMI_CC_UNSPECIFIED_ERROR;
William A. Kennington III5325f2c2018-01-08 15:17:09 -080044 }
William A. Kennington III52575252018-02-09 15:54:56 -080045 catch (...)
46 {
47 log<level::ERR>("wd_reset: Unknown Error");
48 return IPMI_CC_UNSPECIFIED_ERROR;
William A. Kennington III5325f2c2018-01-08 15:17:09 -080049 }
Patrick Venture5e6ac712017-10-25 12:16:19 -070050}
William A. Kennington III61d5f7b2018-02-09 15:23:53 -080051
William A. Kennington III52575252018-02-09 15:54:56 -080052static constexpr uint8_t wd_dont_stop = 0x1 << 6;
53static constexpr uint8_t wd_timeout_action_mask = 0x3;
54
55enum class IpmiAction : uint8_t {
56 None = 0x0,
57 HardReset = 0x1,
58 PowerOff = 0x2,
59 PowerCycle = 0x3,
60};
61
62struct wd_set_req {
63 uint8_t timer_use;
64 uint8_t timer_action;
65 uint8_t pretimeout; // (seconds)
66 uint8_t expire_flags;
67 uint16_t initial_countdown; // Little Endian (deciseconds)
68} __attribute__ ((packed));
69static_assert(sizeof(wd_set_req) == 6, "wd_set_req has invalid size.");
70static_assert(sizeof(wd_set_req) <= MAX_IPMI_BUFFER,
71 "wd_get_res can't fit in request buffer.");
72
William A. Kennington III61d5f7b2018-02-09 15:23:53 -080073ipmi_ret_t ipmi_app_watchdog_set(
74 ipmi_netfn_t netfn,
75 ipmi_cmd_t cmd,
76 ipmi_request_t request,
77 ipmi_response_t response,
78 ipmi_data_len_t data_len,
79 ipmi_context_t context)
80{
William A. Kennington III52575252018-02-09 15:54:56 -080081 // Extract the request data
82 if (*data_len < sizeof(wd_set_req))
83 {
84 *data_len = 0;
85 return IPMI_CC_REQ_DATA_LEN_INVALID;
86 }
87 wd_set_req req;
88 memcpy(&req, request, sizeof(req));
89 req.initial_countdown = le16toh(req.initial_countdown);
William A. Kennington III61d5f7b2018-02-09 15:23:53 -080090 *data_len = 0;
91
William A. Kennington III52575252018-02-09 15:54:56 -080092 try
William A. Kennington III61d5f7b2018-02-09 15:23:53 -080093 {
William A. Kennington III52575252018-02-09 15:54:56 -080094 WatchdogService wd_service;
95 // Stop the timer if the don't stop bit is not set
96 if (!(req.timer_use & wd_dont_stop))
97 {
98 wd_service.setEnabled(false);
William A. Kennington III61d5f7b2018-02-09 15:23:53 -080099 }
100
William A. Kennington III52575252018-02-09 15:54:56 -0800101 // Set the action based on the request
102 // Unfortunately we only really support enable or disable
103 // and don't actually support a real action. Until we have proper
104 // action support just map NONE as a disable action.
105 const auto ipmi_action = static_cast<IpmiAction>(
106 req.timer_action & wd_timeout_action_mask);
107 if (ipmi_action == IpmiAction::None)
108 {
109 wd_service.setEnabled(false);
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800110 }
William A. Kennington III52575252018-02-09 15:54:56 -0800111
112 // Set the new interval and the time remaining deci -> mill seconds
113 const uint64_t interval = req.initial_countdown * 100;
114 wd_service.setInterval(interval);
115 wd_service.setTimeRemaining(interval);
116
117 return IPMI_CC_OK;
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800118 }
William A. Kennington III52575252018-02-09 15:54:56 -0800119 catch (const std::domain_error &)
120 {
121 return IPMI_CC_INVALID_FIELD_REQUEST;
122 }
123 catch (const std::exception& e)
124 {
125 const std::string e_str = std::string("wd_set: ") + e.what();
126 log<level::ERR>(e_str.c_str());
127 return IPMI_CC_UNSPECIFIED_ERROR;
128 }
129 catch (...)
130 {
131 log<level::ERR>("wd_set: Unknown Error");
132 return IPMI_CC_UNSPECIFIED_ERROR;
133 }
William A. Kennington III61d5f7b2018-02-09 15:23:53 -0800134}