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