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