blob: 90e932b175f562bd77b870402d09af8bdef531ad [file] [log] [blame]
Patrick Venture0b02be92018-08-31 11:55:55 -07001#include <config.h>
2#include <systemintfcmds.h>
3
Vishwanatha Subbannaac149a92017-07-11 18:16:50 +05304#include <chrono>
Patrick Venture0b02be92018-08-31 11:55:55 -07005#include <host-cmd-manager.hpp>
Vishwanatha Subbannaac149a92017-07-11 18:16:50 +05306#include <phosphor-logging/elog-errors.hpp>
Patrick Venture0b02be92018-08-31 11:55:55 -07007#include <phosphor-logging/log.hpp>
8#include <timer.hpp>
9#include <utils.hpp>
Vishwanatha Subbannaac149a92017-07-11 18:16:50 +053010#include <xyz/openbmc_project/Common/error.hpp>
Matt Spinler15309ef2018-06-27 13:01:25 -050011#include <xyz/openbmc_project/State/Host/server.hpp>
Vishwanatha Subbanna6e8979d2017-07-13 16:48:20 +053012
Vishwanatha Subbannaac149a92017-07-11 18:16:50 +053013namespace phosphor
14{
15namespace host
16{
17namespace command
18{
19
20constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
21constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper";
22constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper";
Matt Spinler15309ef2018-06-27 13:01:25 -050023constexpr auto HOST_STATE_PATH = "/xyz/openbmc_project/state/host0";
24constexpr auto HOST_STATE_INTERFACE = "xyz.openbmc_project.State.Host";
25constexpr auto HOST_TRANS_PROP = "RequestedHostTransition";
Vishwanatha Subbannaac149a92017-07-11 18:16:50 +053026
27// For throwing exceptions
28using namespace phosphor::logging;
Patrick Venture0b02be92018-08-31 11:55:55 -070029using InternalFailure =
30 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
Vishwanatha Subbannaac149a92017-07-11 18:16:50 +053031
Matt Spinler15309ef2018-06-27 13:01:25 -050032namespace sdbusRule = sdbusplus::bus::match::rules;
33
Vishwanatha Subbanna6e8979d2017-07-13 16:48:20 +053034Manager::Manager(sdbusplus::bus::bus& bus, sd_event* event) :
Patrick Venture0b02be92018-08-31 11:55:55 -070035 bus(bus), timer(event, std::bind(&Manager::hostTimeout, this)),
36 hostTransitionMatch(
37 bus,
38 sdbusRule::propertiesChanged(HOST_STATE_PATH, HOST_STATE_INTERFACE),
39 std::bind(&Manager::clearQueueOnPowerOn, this, std::placeholders::_1))
Vishwanatha Subbanna6e8979d2017-07-13 16:48:20 +053040{
41 // Nothing to do here.
42}
43
Vishwanatha Subbannaac149a92017-07-11 18:16:50 +053044// Called as part of READ_MSG_DATA command
45IpmiCmdData Manager::getNextCommand()
46{
47 // Stop the timer. Don't have to Err failure doing so.
48 auto r = timer.setTimer(SD_EVENT_OFF);
49 if (r < 0)
50 {
51 log<level::ERR>("Failure to STOP the timer",
Patrick Venture0b02be92018-08-31 11:55:55 -070052 entry("ERROR=%s", strerror(-r)));
Vishwanatha Subbannaac149a92017-07-11 18:16:50 +053053 }
54
Patrick Venture0b02be92018-08-31 11:55:55 -070055 if (this->workQueue.empty())
Vishwanatha Subbannaac149a92017-07-11 18:16:50 +053056 {
57 // Just return a heartbeat in this case. A spurious SMS_ATN was
58 // asserted for the host (probably from a previous boot).
Aditya Saripalli5fb14602017-11-09 14:46:27 +053059 log<level::DEBUG>("Control Host work queue is empty!");
Vishwanatha Subbannaac149a92017-07-11 18:16:50 +053060
61 return std::make_pair(CMD_HEARTBEAT, 0x00);
62 }
63
64 // Pop the processed entry off the queue
65 auto command = this->workQueue.front();
66 this->workQueue.pop();
67
68 // IPMI command is the first element in pair
69 auto ipmiCmdData = std::get<0>(command);
70
71 // Now, call the user registered functions so that
72 // implementation specific CommandComplete signals
73 // can be sent. `true` indicating Success.
74 std::get<CallBack>(command)(ipmiCmdData, true);
75
76 // Check for another entry in the queue and kick it off
77 this->checkQueueAndAlertHost();
78
79 // Tuple of command and data
80 return ipmiCmdData;
81}
82
83// Called when initial timer goes off post sending SMS_ATN
84void Manager::hostTimeout()
85{
86 log<level::ERR>("Host control timeout hit!");
87
Matt Spinler15309ef2018-06-27 13:01:25 -050088 clearQueue();
89}
90
91void Manager::clearQueue()
92{
Vishwanatha Subbannaac149a92017-07-11 18:16:50 +053093 // Dequeue all entries and send fail signal
Patrick Venture0b02be92018-08-31 11:55:55 -070094 while (!this->workQueue.empty())
Vishwanatha Subbannaac149a92017-07-11 18:16:50 +053095 {
96 auto command = this->workQueue.front();
97 this->workQueue.pop();
98
99 // IPMI command is the first element in pair
100 auto ipmiCmdData = std::get<0>(command);
101
102 // Call the implementation specific Command Failure.
103 // `false` indicating Failure
104 std::get<CallBack>(command)(ipmiCmdData, false);
105 }
106}
107
108// Called for alerting the host
109void Manager::checkQueueAndAlertHost()
110{
111 if (this->workQueue.size() >= 1)
112 {
Aditya Saripalli5fb14602017-11-09 14:46:27 +0530113 log<level::DEBUG>("Asserting SMS Attention");
Vishwanatha Subbannaac149a92017-07-11 18:16:50 +0530114
115 std::string IPMI_PATH("/org/openbmc/HostIpmi/1");
116 std::string IPMI_INTERFACE("org.openbmc.HostIpmi");
117
Patrick Venture0b02be92018-08-31 11:55:55 -0700118 auto host = ::ipmi::getService(this->bus, IPMI_INTERFACE, IPMI_PATH);
Vishwanatha Subbannaac149a92017-07-11 18:16:50 +0530119
120 // Start the timer for this transaction
121 auto time = std::chrono::duration_cast<std::chrono::microseconds>(
Patrick Venture0b02be92018-08-31 11:55:55 -0700122 std::chrono::seconds(IPMI_SMS_ATN_ACK_TIMEOUT_SECS));
Vishwanatha Subbannaac149a92017-07-11 18:16:50 +0530123
124 auto r = timer.startTimer(time);
125 if (r < 0)
126 {
127 log<level::ERR>("Error starting timer for control host");
128 return;
129 }
130
Patrick Venture0b02be92018-08-31 11:55:55 -0700131 auto method =
132 this->bus.new_method_call(host.c_str(), IPMI_PATH.c_str(),
133 IPMI_INTERFACE.c_str(), "setAttention");
Vishwanatha Subbannaac149a92017-07-11 18:16:50 +0530134 auto reply = this->bus.call(method);
135
136 if (reply.is_method_error())
137 {
138 log<level::ERR>("Error in setting SMS attention");
139 elog<InternalFailure>();
140 }
Aditya Saripalli5fb14602017-11-09 14:46:27 +0530141 log<level::DEBUG>("SMS Attention asserted");
Vishwanatha Subbannaac149a92017-07-11 18:16:50 +0530142 }
143}
144
145// Called by specific implementations that provide commands
146void Manager::execute(CommandHandler command)
147{
Aditya Saripalli5fb14602017-11-09 14:46:27 +0530148 log<level::DEBUG>("Pushing cmd on to queue",
Patrick Venture0b02be92018-08-31 11:55:55 -0700149 entry("COMMAND=%d", std::get<0>(command).first));
Vishwanatha Subbannaac149a92017-07-11 18:16:50 +0530150
151 this->workQueue.emplace(command);
152
153 // Alert host if this is only command in queue otherwise host will
154 // be notified of next message after processing the current one
155 if (this->workQueue.size() == 1)
156 {
157 this->checkQueueAndAlertHost();
158 }
159 else
160 {
161 log<level::INFO>("Command in process, no attention");
162 }
163
164 return;
165}
166
Matt Spinler15309ef2018-06-27 13:01:25 -0500167void Manager::clearQueueOnPowerOn(sdbusplus::message::message& msg)
168{
169 namespace server = sdbusplus::xyz::openbmc_project::State::server;
170
171 ::ipmi::DbusInterface interface;
172 ::ipmi::PropertyMap properties;
173
174 msg.read(interface, properties);
175
176 if (properties.find(HOST_TRANS_PROP) == properties.end())
177 {
178 return;
179 }
180
181 auto& requestedState = properties.at(HOST_TRANS_PROP).get<std::string>();
182
183 if (server::Host::convertTransitionFromString(requestedState) ==
Patrick Venture0b02be92018-08-31 11:55:55 -0700184 server::Host::Transition::On)
Matt Spinler15309ef2018-06-27 13:01:25 -0500185 {
186 clearQueue();
187 }
188}
189
Vishwanatha Subbannaac149a92017-07-11 18:16:50 +0530190} // namespace command
191} // namespace host
Patrick Venture0b02be92018-08-31 11:55:55 -0700192} // namespace phosphor