xref: /openbmc/phosphor-host-ipmid/host-cmd-manager.cpp (revision 76bfd7563b83f7cf04e27465cbf2b9246c09e28a)
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 using HostState = sdbusplus::common::xyz::openbmc_project::state::Host;
18 
19 namespace phosphor
20 {
21 namespace host
22 {
23 namespace command
24 {
25 
26 // For throwing exceptions
27 using namespace phosphor::logging;
28 using InternalFailure =
29     sdbusplus::error::xyz::openbmc_project::common::InternalFailure;
30 
31 namespace sdbusRule = sdbusplus::bus::match::rules;
32 
Manager(sdbusplus::bus_t & bus)33 Manager::Manager(sdbusplus::bus_t& bus) :
34     bus(bus), timer(std::bind(&Manager::hostTimeout, this)),
35     hostTransitionMatch(
36         bus,
37         sdbusRule::propertiesChanged("/xyz/openbmc_project/state/host0",
38                                      HostState::interface),
39         std::bind(&Manager::clearQueueOnPowerOn, this, std::placeholders::_1))
40 {
41     // Nothing to do here.
42 }
43 
44 // Called as part of READ_MSG_DATA command
getNextCommand()45 IpmiCmdData Manager::getNextCommand()
46 {
47     // Stop the timer. Don't have to Err failure doing so.
48     auto r = timer.stop();
49     if (r < 0)
50     {
51         lg2::error("Failure to STOP the timer: {ERROR}", "ERROR", strerror(-r));
52     }
53 
54     if (this->workQueue.empty())
55     {
56         // Just return a heartbeat in this case.  A spurious SMS_ATN was
57         // asserted for the host (probably from a previous boot).
58         lg2::debug("Control Host work queue is empty!");
59 
60         return std::make_pair(CMD_HEARTBEAT, 0x00);
61     }
62 
63     // Pop the processed entry off the queue
64     auto command = this->workQueue.front();
65     this->workQueue.pop();
66 
67     // IPMI command is the first element in pair
68     auto ipmiCmdData = std::get<0>(command);
69 
70     // Now, call the user registered functions so that
71     // implementation specific CommandComplete signals
72     // can be sent. `true` indicating Success.
73     std::get<CallBack>(command)(ipmiCmdData, true);
74 
75     // Check for another entry in the queue and kick it off
76     this->checkQueueAndAlertHost();
77 
78     // Tuple of command and data
79     return ipmiCmdData;
80 }
81 
82 // Called when initial timer goes off post sending SMS_ATN
hostTimeout()83 void Manager::hostTimeout()
84 {
85     lg2::error("Host control timeout hit!");
86 
87     clearQueue();
88 }
89 
clearQueue()90 void Manager::clearQueue()
91 {
92     // Dequeue all entries and send fail signal
93     while (!this->workQueue.empty())
94     {
95         auto command = this->workQueue.front();
96         this->workQueue.pop();
97 
98         // IPMI command is the first element in pair
99         auto ipmiCmdData = std::get<0>(command);
100 
101         // Call the implementation specific Command Failure.
102         // `false` indicating Failure
103         std::get<CallBack>(command)(ipmiCmdData, false);
104     }
105 }
106 
107 // Called for alerting the host
checkQueueAndAlertHost()108 void Manager::checkQueueAndAlertHost()
109 {
110     if (this->workQueue.size() >= 1)
111     {
112         lg2::debug("Asserting SMS Attention");
113 
114         std::string HOST_IPMI_SVC("org.openbmc.HostIpmi");
115         std::string IPMI_PATH("/org/openbmc/HostIpmi/1");
116         std::string IPMI_INTERFACE("org.openbmc.HostIpmi");
117 
118         // Start the timer for this transaction
119         auto time = std::chrono::duration_cast<std::chrono::microseconds>(
120             std::chrono::seconds(IPMI_SMS_ATN_ACK_TIMEOUT_SECS));
121 
122         auto r = timer.start(time);
123         if (r < 0)
124         {
125             lg2::error("Error starting timer for control host");
126             return;
127         }
128 
129         auto method =
130             this->bus.new_method_call(HOST_IPMI_SVC.c_str(), IPMI_PATH.c_str(),
131                                       IPMI_INTERFACE.c_str(), "setAttention");
132 
133         try
134         {
135             auto reply = this->bus.call(method);
136 
137             lg2::debug("SMS Attention asserted");
138         }
139         catch (sdbusplus::exception_t& e)
140         {
141             lg2::error("Error when call setAttention method");
142         }
143     }
144 }
145 
146 // Called by specific implementations that provide commands
execute(CommandHandler command)147 void Manager::execute(CommandHandler command)
148 {
149     lg2::debug("Pushing cmd on to queue, command: {COMMAND}", "COMMAND",
150                std::get<0>(command).first);
151 
152     this->workQueue.emplace(command);
153 
154     // Alert host if this is only command in queue otherwise host will
155     // be notified of next message after processing the current one
156     if (this->workQueue.size() == 1)
157     {
158         this->checkQueueAndAlertHost();
159     }
160     else
161     {
162         lg2::info("Command in process, no attention");
163     }
164 
165     return;
166 }
167 
clearQueueOnPowerOn(sdbusplus::message_t & msg)168 void Manager::clearQueueOnPowerOn(sdbusplus::message_t& msg)
169 {
170     namespace server = sdbusplus::server::xyz::openbmc_project::state;
171 
172     ::ipmi::DbusInterface interface;
173     ::ipmi::PropertyMap properties;
174 
175     msg.read(interface, properties);
176 
177     if (properties.find(HostState::property_names::requested_host_transition) ==
178         properties.end())
179     {
180         return;
181     }
182 
183     auto& requestedState = std::get<std::string>(
184         properties.at(HostState::property_names::requested_host_transition));
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