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