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