1 2 #include "config.h" 3 4 #include "host-interface.hpp" 5 6 #include "systemintfcmds.hpp" 7 8 #include <functional> 9 #include <ipmid-host/cmd-utils.hpp> 10 #include <ipmid-host/cmd.hpp> 11 #include <ipmid/api.hpp> 12 #include <ipmid/utils.hpp> 13 #include <memory> 14 #include <optional> 15 #include <phosphor-logging/log.hpp> 16 17 namespace phosphor 18 { 19 namespace host 20 { 21 namespace command 22 { 23 24 using namespace phosphor::logging; 25 26 // When you see Base:: you know we're referencing our base class 27 namespace Base = sdbusplus::xyz::openbmc_project::Control::server; 28 29 // IPMI OEM command. 30 // https://github.com/openbmc/openbmc/issues/2082 for handling 31 // Non-OEM commands that need to send SMS_ATN 32 using OEMCmd = uint8_t; 33 34 // Map of IPMI OEM command to its equivalent interface command. 35 // This is needed when invoking the callback handler to indicate 36 // the status of the executed command. 37 static const std::map<OEMCmd, Host::Command> intfCommand = { 38 {CMD_HEARTBEAT, Base::Host::Command::Heartbeat}, 39 {CMD_POWER, Base::Host::Command::SoftOff}}; 40 41 // Map of Interface command to its corresponding IPMI OEM command. 42 // This is needed when pushing IPMI commands to command manager's 43 // queue. The same pair will be returned when IPMI asks us 44 // why a SMS_ATN was sent 45 static const std::map<Host::Command, IpmiCmdData> ipmiCommand = { 46 {Base::Host::Command::Heartbeat, std::make_pair(CMD_HEARTBEAT, 0x00)}, 47 {Base::Host::Command::SoftOff, std::make_pair(CMD_POWER, SOFT_OFF)}}; 48 49 // Called at user request 50 void Host::execute(Base::Host::Command command) 51 { 52 log<level::DEBUG>( 53 "Pushing cmd on to queue", 54 entry("CONTROL_HOST_CMD=%s", convertForMessage(command).c_str())); 55 56 auto cmd = std::make_tuple(ipmiCommand.at(command), 57 std::bind(&Host::commandStatusHandler, this, 58 std::placeholders::_1, 59 std::placeholders::_2)); 60 61 ipmid_send_cmd_to_host(std::move(cmd)); 62 } 63 64 // Called into by Command Manager 65 void Host::commandStatusHandler(IpmiCmdData cmd, bool status) 66 { 67 // Need to convert <cmd> to the equivalent one mentioned in spec 68 auto value = status ? Result::Success : Result::Failure; 69 70 // Fire a signal 71 this->commandComplete(intfCommand.at(std::get<0>(cmd)), value); 72 } 73 74 Host::FirmwareCondition Host::currentFirmwareCondition() const 75 { 76 // shared object used to wait for host response 77 auto hostCondition = 78 std::make_shared<std::optional<Host::FirmwareCondition>>(); 79 80 // callback for command to host 81 auto hostAckCallback = [hostCondition](IpmiCmdData cmd, bool status) { 82 auto value = status ? Host::FirmwareCondition::Running 83 : Host::FirmwareCondition::Off; 84 85 log<level::DEBUG>("currentFirmwareCondition:hostAckCallback fired", 86 entry("CONTROL_HOST_CMD=%i", value)); 87 88 *(hostCondition.get()) = value; 89 return; 90 }; 91 92 auto cmd = phosphor::host::command::CommandHandler( 93 ipmiCommand.at(Base::Host::Command::Heartbeat), 94 std::move(hostAckCallback)); 95 96 ipmid_send_cmd_to_host(std::move(cmd)); 97 98 // Timer to ensure this function returns something within a reasonable time 99 phosphor::Timer hostAckTimer([hostCondition]() { 100 log<level::DEBUG>("currentFirmwareCondition: timer expired!"); 101 *(hostCondition.get()) = Host::FirmwareCondition::Off; 102 }); 103 104 // Wait 1 second past the ATN_ACK timeout to ensure we wait for as 105 // long as the timeout 106 hostAckTimer.start(std::chrono::seconds(IPMI_SMS_ATN_ACK_TIMEOUT_SECS + 1)); 107 108 auto io = getIoContext(); 109 110 while (!hostCondition.get()->has_value()) 111 { 112 log<level::DEBUG>( 113 "currentFirmwareCondition: waiting for host response"); 114 io->run_for(std::chrono::milliseconds(100)); 115 } 116 hostAckTimer.stop(); 117 118 log<level::DEBUG>("currentFirmwareCondition: hostCondition is ready!"); 119 return hostCondition.get()->value(); 120 } 121 122 } // namespace command 123 } // namespace host 124 } // namespace phosphor 125