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