1 #include <chrono>
2 #include <phosphor-logging/log.hpp>
3 #include <phosphor-logging/elog-errors.hpp>
4 #include <xyz/openbmc_project/Common/error.hpp>
5 #include <systemintfcmds.h>
6 #include <utils.hpp>
7 #include <config.h>
8 #include <host-cmd-manager.hpp>
9 #include <timer.hpp>
10 
11 namespace phosphor
12 {
13 namespace host
14 {
15 namespace command
16 {
17 
18 constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
19 constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper";
20 constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper";
21 
22 // For throwing exceptions
23 using namespace phosphor::logging;
24 using InternalFailure = sdbusplus::xyz::openbmc_project::Common::
25                             Error::InternalFailure;
26 
27 Manager::Manager(sdbusplus::bus::bus& bus, sd_event* event) :
28             bus(bus),
29             timer(event, std::bind(&Manager::hostTimeout, this))
30 {
31     // Nothing to do here.
32 }
33 
34 // Called as part of READ_MSG_DATA command
35 IpmiCmdData Manager::getNextCommand()
36 {
37     // Stop the timer. Don't have to Err failure doing so.
38     auto r = timer.setTimer(SD_EVENT_OFF);
39     if (r < 0)
40     {
41         log<level::ERR>("Failure to STOP the timer",
42                 entry("ERROR=%s", strerror(-r)));
43     }
44 
45     if(this->workQueue.empty())
46     {
47         // Just return a heartbeat in this case.  A spurious SMS_ATN was
48         // asserted for the host (probably from a previous boot).
49         log<level::INFO>("Control Host work queue is empty!");
50 
51         return std::make_pair(CMD_HEARTBEAT, 0x00);
52     }
53 
54     // Pop the processed entry off the queue
55     auto command = this->workQueue.front();
56     this->workQueue.pop();
57 
58     // IPMI command is the first element in pair
59     auto ipmiCmdData = std::get<0>(command);
60 
61     // Now, call the user registered functions so that
62     // implementation specific CommandComplete signals
63     // can be sent. `true` indicating Success.
64     std::get<CallBack>(command)(ipmiCmdData, true);
65 
66     // Check for another entry in the queue and kick it off
67     this->checkQueueAndAlertHost();
68 
69     // Tuple of command and data
70     return ipmiCmdData;
71 }
72 
73 // Called when initial timer goes off post sending SMS_ATN
74 void Manager::hostTimeout()
75 {
76     log<level::ERR>("Host control timeout hit!");
77 
78     // Dequeue all entries and send fail signal
79     while(!this->workQueue.empty())
80     {
81         auto command = this->workQueue.front();
82         this->workQueue.pop();
83 
84         // IPMI command is the first element in pair
85         auto ipmiCmdData = std::get<0>(command);
86 
87         // Call the implementation specific Command Failure.
88         // `false` indicating Failure
89         std::get<CallBack>(command)(ipmiCmdData, false);
90     }
91 }
92 
93 // Called for alerting the host
94 void Manager::checkQueueAndAlertHost()
95 {
96     if (this->workQueue.size() >= 1)
97     {
98         log<level::INFO>("Asserting SMS Attention");
99 
100         std::string IPMI_PATH("/org/openbmc/HostIpmi/1");
101         std::string IPMI_INTERFACE("org.openbmc.HostIpmi");
102 
103         auto host = ::ipmi::getService(this->bus,IPMI_INTERFACE,IPMI_PATH);
104 
105         // Start the timer for this transaction
106         auto time = std::chrono::duration_cast<std::chrono::microseconds>(
107                         std::chrono::seconds(IPMI_SMS_ATN_ACK_TIMEOUT_SECS));
108 
109         auto r = timer.startTimer(time);
110         if (r < 0)
111         {
112             log<level::ERR>("Error starting timer for control host");
113             return;
114         }
115 
116         auto method = this->bus.new_method_call(host.c_str(),
117                                                 IPMI_PATH.c_str(),
118                                                 IPMI_INTERFACE.c_str(),
119                                                 "setAttention");
120         auto reply = this->bus.call(method);
121 
122         if (reply.is_method_error())
123         {
124             log<level::ERR>("Error in setting SMS attention");
125             elog<InternalFailure>();
126         }
127         log<level::INFO>("SMS Attention asserted");
128     }
129 }
130 
131 // Called by specific implementations that provide commands
132 void Manager::execute(CommandHandler command)
133 {
134     log<level::INFO>("Pushing cmd on to queue",
135             entry("COMMAND=%d", std::get<0>(command).first));
136 
137     this->workQueue.emplace(command);
138 
139     // Alert host if this is only command in queue otherwise host will
140     // be notified of next message after processing the current one
141     if (this->workQueue.size() == 1)
142     {
143         this->checkQueueAndAlertHost();
144     }
145     else
146     {
147         log<level::INFO>("Command in process, no attention");
148     }
149 
150     return;
151 }
152 
153 } // namespace command
154 } // namespace host
155 } // namepsace phosphor
156