blob: 408491019690422d6945915a8a0ad867449f8d4e [file] [log] [blame]
Patrick Venture46470a32018-09-07 19:26:25 -07001#include "config.h"
2
3#include "host-cmd-manager.hpp"
4
5#include "systemintfcmds.hpp"
6#include "timer.hpp"
7#include "utils.hpp"
Patrick Venture0b02be92018-08-31 11:55:55 -07008
Vishwanatha Subbannaac149a92017-07-11 18:16:50 +05309#include <chrono>
Vishwanatha Subbannaac149a92017-07-11 18:16:50 +053010#include <phosphor-logging/elog-errors.hpp>
Patrick Venture0b02be92018-08-31 11:55:55 -070011#include <phosphor-logging/log.hpp>
Vishwanatha Subbannaac149a92017-07-11 18:16:50 +053012#include <xyz/openbmc_project/Common/error.hpp>
Matt Spinler15309ef2018-06-27 13:01:25 -050013#include <xyz/openbmc_project/State/Host/server.hpp>
Vishwanatha Subbanna6e8979d2017-07-13 16:48:20 +053014
Vishwanatha Subbannaac149a92017-07-11 18:16:50 +053015namespace phosphor
16{
17namespace host
18{
19namespace command
20{
21
22constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
23constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper";
24constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper";
Matt Spinler15309ef2018-06-27 13:01:25 -050025constexpr auto HOST_STATE_PATH = "/xyz/openbmc_project/state/host0";
26constexpr auto HOST_STATE_INTERFACE = "xyz.openbmc_project.State.Host";
27constexpr auto HOST_TRANS_PROP = "RequestedHostTransition";
Vishwanatha Subbannaac149a92017-07-11 18:16:50 +053028
29// For throwing exceptions
30using namespace phosphor::logging;
Patrick Venture0b02be92018-08-31 11:55:55 -070031using InternalFailure =
32 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
Vishwanatha Subbannaac149a92017-07-11 18:16:50 +053033
Matt Spinler15309ef2018-06-27 13:01:25 -050034namespace sdbusRule = sdbusplus::bus::match::rules;
35
Vishwanatha Subbanna6e8979d2017-07-13 16:48:20 +053036Manager::Manager(sdbusplus::bus::bus& bus, sd_event* event) :
Patrick Venture0b02be92018-08-31 11:55:55 -070037 bus(bus), timer(event, std::bind(&Manager::hostTimeout, this)),
38 hostTransitionMatch(
39 bus,
40 sdbusRule::propertiesChanged(HOST_STATE_PATH, HOST_STATE_INTERFACE),
41 std::bind(&Manager::clearQueueOnPowerOn, this, std::placeholders::_1))
Vishwanatha Subbanna6e8979d2017-07-13 16:48:20 +053042{
43 // Nothing to do here.
44}
45
Vishwanatha Subbannaac149a92017-07-11 18:16:50 +053046// Called as part of READ_MSG_DATA command
47IpmiCmdData Manager::getNextCommand()
48{
49 // Stop the timer. Don't have to Err failure doing so.
50 auto r = timer.setTimer(SD_EVENT_OFF);
51 if (r < 0)
52 {
53 log<level::ERR>("Failure to STOP the timer",
Patrick Venture0b02be92018-08-31 11:55:55 -070054 entry("ERROR=%s", strerror(-r)));
Vishwanatha Subbannaac149a92017-07-11 18:16:50 +053055 }
56
Patrick Venture0b02be92018-08-31 11:55:55 -070057 if (this->workQueue.empty())
Vishwanatha Subbannaac149a92017-07-11 18:16:50 +053058 {
59 // Just return a heartbeat in this case. A spurious SMS_ATN was
60 // asserted for the host (probably from a previous boot).
Aditya Saripalli5fb14602017-11-09 14:46:27 +053061 log<level::DEBUG>("Control Host work queue is empty!");
Vishwanatha Subbannaac149a92017-07-11 18:16:50 +053062
63 return std::make_pair(CMD_HEARTBEAT, 0x00);
64 }
65
66 // Pop the processed entry off the queue
67 auto command = this->workQueue.front();
68 this->workQueue.pop();
69
70 // IPMI command is the first element in pair
71 auto ipmiCmdData = std::get<0>(command);
72
73 // Now, call the user registered functions so that
74 // implementation specific CommandComplete signals
75 // can be sent. `true` indicating Success.
76 std::get<CallBack>(command)(ipmiCmdData, true);
77
78 // Check for another entry in the queue and kick it off
79 this->checkQueueAndAlertHost();
80
81 // Tuple of command and data
82 return ipmiCmdData;
83}
84
85// Called when initial timer goes off post sending SMS_ATN
86void Manager::hostTimeout()
87{
88 log<level::ERR>("Host control timeout hit!");
89
Matt Spinler15309ef2018-06-27 13:01:25 -050090 clearQueue();
91}
92
93void Manager::clearQueue()
94{
Vishwanatha Subbannaac149a92017-07-11 18:16:50 +053095 // Dequeue all entries and send fail signal
Patrick Venture0b02be92018-08-31 11:55:55 -070096 while (!this->workQueue.empty())
Vishwanatha Subbannaac149a92017-07-11 18:16:50 +053097 {
98 auto command = this->workQueue.front();
99 this->workQueue.pop();
100
101 // IPMI command is the first element in pair
102 auto ipmiCmdData = std::get<0>(command);
103
104 // Call the implementation specific Command Failure.
105 // `false` indicating Failure
106 std::get<CallBack>(command)(ipmiCmdData, false);
107 }
108}
109
110// Called for alerting the host
111void Manager::checkQueueAndAlertHost()
112{
113 if (this->workQueue.size() >= 1)
114 {
Aditya Saripalli5fb14602017-11-09 14:46:27 +0530115 log<level::DEBUG>("Asserting SMS Attention");
Vishwanatha Subbannaac149a92017-07-11 18:16:50 +0530116
117 std::string IPMI_PATH("/org/openbmc/HostIpmi/1");
118 std::string IPMI_INTERFACE("org.openbmc.HostIpmi");
119
Patrick Venture0b02be92018-08-31 11:55:55 -0700120 auto host = ::ipmi::getService(this->bus, IPMI_INTERFACE, IPMI_PATH);
Vishwanatha Subbannaac149a92017-07-11 18:16:50 +0530121
122 // Start the timer for this transaction
123 auto time = std::chrono::duration_cast<std::chrono::microseconds>(
Patrick Venture0b02be92018-08-31 11:55:55 -0700124 std::chrono::seconds(IPMI_SMS_ATN_ACK_TIMEOUT_SECS));
Vishwanatha Subbannaac149a92017-07-11 18:16:50 +0530125
126 auto r = timer.startTimer(time);
127 if (r < 0)
128 {
129 log<level::ERR>("Error starting timer for control host");
130 return;
131 }
132
Patrick Venture0b02be92018-08-31 11:55:55 -0700133 auto method =
134 this->bus.new_method_call(host.c_str(), IPMI_PATH.c_str(),
135 IPMI_INTERFACE.c_str(), "setAttention");
Vishwanatha Subbannaac149a92017-07-11 18:16:50 +0530136 auto reply = this->bus.call(method);
137
138 if (reply.is_method_error())
139 {
140 log<level::ERR>("Error in setting SMS attention");
141 elog<InternalFailure>();
142 }
Aditya Saripalli5fb14602017-11-09 14:46:27 +0530143 log<level::DEBUG>("SMS Attention asserted");
Vishwanatha Subbannaac149a92017-07-11 18:16:50 +0530144 }
145}
146
147// Called by specific implementations that provide commands
148void Manager::execute(CommandHandler command)
149{
Aditya Saripalli5fb14602017-11-09 14:46:27 +0530150 log<level::DEBUG>("Pushing cmd on to queue",
Patrick Venture0b02be92018-08-31 11:55:55 -0700151 entry("COMMAND=%d", std::get<0>(command).first));
Vishwanatha Subbannaac149a92017-07-11 18:16:50 +0530152
153 this->workQueue.emplace(command);
154
155 // Alert host if this is only command in queue otherwise host will
156 // be notified of next message after processing the current one
157 if (this->workQueue.size() == 1)
158 {
159 this->checkQueueAndAlertHost();
160 }
161 else
162 {
163 log<level::INFO>("Command in process, no attention");
164 }
165
166 return;
167}
168
Matt Spinler15309ef2018-06-27 13:01:25 -0500169void Manager::clearQueueOnPowerOn(sdbusplus::message::message& msg)
170{
171 namespace server = sdbusplus::xyz::openbmc_project::State::server;
172
173 ::ipmi::DbusInterface interface;
174 ::ipmi::PropertyMap properties;
175
176 msg.read(interface, properties);
177
178 if (properties.find(HOST_TRANS_PROP) == properties.end())
179 {
180 return;
181 }
182
183 auto& requestedState = properties.at(HOST_TRANS_PROP).get<std::string>();
184
185 if (server::Host::convertTransitionFromString(requestedState) ==
Patrick Venture0b02be92018-08-31 11:55:55 -0700186 server::Host::Transition::On)
Matt Spinler15309ef2018-06-27 13:01:25 -0500187 {
188 clearQueue();
189 }
190}
191
Vishwanatha Subbannaac149a92017-07-11 18:16:50 +0530192} // namespace command
193} // namespace host
Patrick Venture0b02be92018-08-31 11:55:55 -0700194} // namespace phosphor