1 #include "config.h" 2 3 #include "host-cmd-manager.hpp" 4 5 #include "systemintfcmds.hpp" 6 7 #include <chrono> 8 #include <ipmid/utils.hpp> 9 #include <phosphor-logging/elog-errors.hpp> 10 #include <phosphor-logging/log.hpp> 11 #include <sdbusplus/message/types.hpp> 12 #include <sdbusplus/timer.hpp> 13 #include <xyz/openbmc_project/Common/error.hpp> 14 #include <xyz/openbmc_project/State/Host/server.hpp> 15 16 namespace phosphor 17 { 18 namespace host 19 { 20 namespace command 21 { 22 23 constexpr auto HOST_STATE_PATH = "/xyz/openbmc_project/state/host0"; 24 constexpr auto HOST_STATE_INTERFACE = "xyz.openbmc_project.State.Host"; 25 constexpr auto HOST_TRANS_PROP = "RequestedHostTransition"; 26 27 // For throwing exceptions 28 using namespace phosphor::logging; 29 using InternalFailure = 30 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 31 32 namespace sdbusRule = sdbusplus::bus::match::rules; 33 34 Manager::Manager(sdbusplus::bus_t& bus) : 35 bus(bus), timer(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)) 40 { 41 // Nothing to do here. 42 } 43 44 // Called as part of READ_MSG_DATA command 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 log<level::ERR>("Failure to STOP the timer", 52 entry("ERROR=%s", 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 log<level::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 84 void Manager::hostTimeout() 85 { 86 log<level::ERR>("Host control timeout hit!"); 87 88 clearQueue(); 89 } 90 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 109 void Manager::checkQueueAndAlertHost() 110 { 111 if (this->workQueue.size() >= 1) 112 { 113 log<level::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 log<level::ERR>("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 log<level::DEBUG>("SMS Attention asserted"); 139 } 140 catch (sdbusplus::exception_t& e) 141 { 142 log<level::ERR>("Error when call setAttention method"); 143 } 144 } 145 } 146 147 // Called by specific implementations that provide commands 148 void Manager::execute(CommandHandler command) 149 { 150 log<level::DEBUG>("Pushing cmd on to queue", 151 entry("COMMAND=%d", 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 log<level::INFO>("Command in process, no attention"); 164 } 165 166 return; 167 } 168 169 void Manager::clearQueueOnPowerOn(sdbusplus::message_t& 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 = 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