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