1 #include "config.h"
2 
3 #include "host-cmd-manager.hpp"
4 
5 #include "systemintfcmds.hpp"
6 
7 #include <ipmid/utils.hpp>
8 #include <phosphor-logging/elog-errors.hpp>
9 #include <phosphor-logging/lg2.hpp>
10 #include <sdbusplus/message/types.hpp>
11 #include <sdbusplus/timer.hpp>
12 #include <xyz/openbmc_project/Common/error.hpp>
13 #include <xyz/openbmc_project/State/Host/server.hpp>
14 
15 #include <chrono>
16 
17 namespace phosphor
18 {
19 namespace host
20 {
21 namespace command
22 {
23 
24 constexpr auto HOST_STATE_PATH = "/xyz/openbmc_project/state/host0";
25 constexpr auto HOST_STATE_INTERFACE = "xyz.openbmc_project.State.Host";
26 constexpr auto HOST_TRANS_PROP = "RequestedHostTransition";
27 
28 // For throwing exceptions
29 using namespace phosphor::logging;
30 using InternalFailure =
31     sdbusplus::error::xyz::openbmc_project::common::InternalFailure;
32 
33 namespace sdbusRule = sdbusplus::bus::match::rules;
34 
Manager(sdbusplus::bus_t & bus)35 Manager::Manager(sdbusplus::bus_t& bus) :
36     bus(bus), timer(std::bind(&Manager::hostTimeout, this)),
37     hostTransitionMatch(
38         bus,
39         sdbusRule::propertiesChanged(HOST_STATE_PATH, HOST_STATE_INTERFACE),
40         std::bind(&Manager::clearQueueOnPowerOn, this, std::placeholders::_1))
41 {
42     // Nothing to do here.
43 }
44 
45 // Called as part of READ_MSG_DATA command
getNextCommand()46 IpmiCmdData Manager::getNextCommand()
47 {
48     // Stop the timer. Don't have to Err failure doing so.
49     auto r = timer.stop();
50     if (r < 0)
51     {
52         lg2::error("Failure to STOP the timer: {ERROR}", "ERROR", strerror(-r));
53     }
54 
55     if (this->workQueue.empty())
56     {
57         // Just return a heartbeat in this case.  A spurious SMS_ATN was
58         // asserted for the host (probably from a previous boot).
59         lg2::debug("Control Host work queue is empty!");
60 
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
hostTimeout()84 void Manager::hostTimeout()
85 {
86     lg2::error("Host control timeout hit!");
87 
88     clearQueue();
89 }
90 
clearQueue()91 void Manager::clearQueue()
92 {
93     // Dequeue all entries and send fail signal
94     while (!this->workQueue.empty())
95     {
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
checkQueueAndAlertHost()109 void Manager::checkQueueAndAlertHost()
110 {
111     if (this->workQueue.size() >= 1)
112     {
113         lg2::debug("Asserting SMS Attention");
114 
115         std::string HOST_IPMI_SVC("org.openbmc.HostIpmi");
116         std::string IPMI_PATH("/org/openbmc/HostIpmi/1");
117         std::string IPMI_INTERFACE("org.openbmc.HostIpmi");
118 
119         // Start the timer for this transaction
120         auto time = std::chrono::duration_cast<std::chrono::microseconds>(
121             std::chrono::seconds(IPMI_SMS_ATN_ACK_TIMEOUT_SECS));
122 
123         auto r = timer.start(time);
124         if (r < 0)
125         {
126             lg2::error("Error starting timer for control host");
127             return;
128         }
129 
130         auto method =
131             this->bus.new_method_call(HOST_IPMI_SVC.c_str(), IPMI_PATH.c_str(),
132                                       IPMI_INTERFACE.c_str(), "setAttention");
133 
134         try
135         {
136             auto reply = this->bus.call(method);
137 
138             lg2::debug("SMS Attention asserted");
139         }
140         catch (sdbusplus::exception_t& e)
141         {
142             lg2::error("Error when call setAttention method");
143         }
144     }
145 }
146 
147 // Called by specific implementations that provide commands
execute(CommandHandler command)148 void Manager::execute(CommandHandler command)
149 {
150     lg2::debug("Pushing cmd on to queue, command: {COMMAND}", "COMMAND",
151                std::get<0>(command).first);
152 
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         lg2::info("Command in process, no attention");
164     }
165 
166     return;
167 }
168 
clearQueueOnPowerOn(sdbusplus::message_t & msg)169 void Manager::clearQueueOnPowerOn(sdbusplus::message_t& msg)
170 {
171     namespace server = sdbusplus::server::xyz::openbmc_project::state;
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 =
184         std::get<std::string>(properties.at(HOST_TRANS_PROP));
185 
186     if (server::Host::convertTransitionFromString(requestedState) ==
187         server::Host::Transition::On)
188     {
189         clearQueue();
190     }
191 }
192 
193 } // namespace command
194 } // namespace host
195 } // namespace phosphor
196