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
execute(Base::Host::Command command)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(
55         ipmiCommand.at(command),
56         std::bind(&Host::commandStatusHandler, this, 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
commandStatusHandler(IpmiCmdData cmd,bool status)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 
currentFirmwareCondition() const72 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