diff options
-rw-r--r-- | Makefile.am | 15 | ||||
-rw-r--r-- | host-cmd-manager.cpp | 147 | ||||
-rw-r--r-- | host-cmd-manager.hpp | 88 | ||||
-rw-r--r-- | host-cmd-utils.hpp | 49 |
4 files changed, 292 insertions, 7 deletions
diff --git a/Makefile.am b/Makefile.am index 18b198c..ea44248 100644 --- a/Makefile.am +++ b/Makefile.am @@ -5,8 +5,10 @@ sbin_PROGRAMS = \ ipmid_SOURCES = \ ipmid.cpp \ - settings.cpp - + settings.cpp \ + host-cmd-manager.cpp \ + timer.cpp \ + utils.cpp nodist_ipmid_SOURCES = ipmiwhitelist.cpp BUILT_SOURCES = \ @@ -18,11 +20,9 @@ BUILT_SOURCES = \ CLEANFILES = $(BUILT_SOURCES) #TODO - Make this path a configure option (bitbake parameter) -ipmid_CPPFLAGS = \ - -DHOST_IPMI_LIB_PATH=\"/usr/lib/host-ipmid/\" \ - $(PHOSPHOR_LOGGING_CFLAGS) \ - $(PHOSPHOR_DBUS_INTERFACES_CFLAGS) - +ipmid_CPPFLAGS = -DHOST_IPMI_LIB_PATH=\"/usr/lib/host-ipmid/\" \ + $(PHOSPHOR_LOGGING_CFLAGS) \ + $(PHOSPHOR_DBUS_INTERFACES_CFLAGS) ipmid_LDFLAGS = \ $(SYSTEMD_LIBS) \ $(libmapper_LIBS) \ @@ -30,6 +30,7 @@ ipmid_LDFLAGS = \ $(PHOSPHOR_LOGGING_LIBS) \ $(PHOSPHOR_DBUS_INTERFACES_LIBS) \ -export-dynamic + # TODO: Rather than use -export-dynamic, we should use -export-symbol to have a # selective list of symbols. diff --git a/host-cmd-manager.cpp b/host-cmd-manager.cpp new file mode 100644 index 0000000..5b75b55 --- /dev/null +++ b/host-cmd-manager.cpp @@ -0,0 +1,147 @@ +#include <chrono> +#include <phosphor-logging/log.hpp> +#include <phosphor-logging/elog-errors.hpp> +#include <xyz/openbmc_project/Common/error.hpp> +#include <systemintfcmds.h> +#include <utils.hpp> +#include <config.h> +#include <host-cmd-manager.hpp> + +namespace phosphor +{ +namespace host +{ +namespace command +{ + +constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper"; +constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper"; +constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper"; + +// For throwing exceptions +using namespace phosphor::logging; +using InternalFailure = sdbusplus::xyz::openbmc_project::Common:: + Error::InternalFailure; + +// Called as part of READ_MSG_DATA command +IpmiCmdData Manager::getNextCommand() +{ + // Stop the timer. Don't have to Err failure doing so. + auto r = timer.setTimer(SD_EVENT_OFF); + if (r < 0) + { + log<level::ERR>("Failure to STOP the timer", + entry("ERROR=%s", strerror(-r))); + } + + if(this->workQueue.empty()) + { + // Just return a heartbeat in this case. A spurious SMS_ATN was + // asserted for the host (probably from a previous boot). + log<level::INFO>("Control Host work queue is empty!"); + + return std::make_pair(CMD_HEARTBEAT, 0x00); + } + + // Pop the processed entry off the queue + auto command = this->workQueue.front(); + this->workQueue.pop(); + + // IPMI command is the first element in pair + auto ipmiCmdData = std::get<0>(command); + + // Now, call the user registered functions so that + // implementation specific CommandComplete signals + // can be sent. `true` indicating Success. + std::get<CallBack>(command)(ipmiCmdData, true); + + // Check for another entry in the queue and kick it off + this->checkQueueAndAlertHost(); + + // Tuple of command and data + return ipmiCmdData; +} + +// Called when initial timer goes off post sending SMS_ATN +void Manager::hostTimeout() +{ + log<level::ERR>("Host control timeout hit!"); + + // Dequeue all entries and send fail signal + while(!this->workQueue.empty()) + { + auto command = this->workQueue.front(); + this->workQueue.pop(); + + // IPMI command is the first element in pair + auto ipmiCmdData = std::get<0>(command); + + // Call the implementation specific Command Failure. + // `false` indicating Failure + std::get<CallBack>(command)(ipmiCmdData, false); + } +} + +// Called for alerting the host +void Manager::checkQueueAndAlertHost() +{ + if (this->workQueue.size() >= 1) + { + log<level::INFO>("Asserting SMS Attention"); + + std::string IPMI_PATH("/org/openbmc/HostIpmi/1"); + std::string IPMI_INTERFACE("org.openbmc.HostIpmi"); + + auto host = ::ipmi::getService(this->bus,IPMI_INTERFACE,IPMI_PATH); + + // Start the timer for this transaction + auto time = std::chrono::duration_cast<std::chrono::microseconds>( + std::chrono::seconds(IPMI_SMS_ATN_ACK_TIMEOUT_SECS)); + + auto r = timer.startTimer(time); + if (r < 0) + { + log<level::ERR>("Error starting timer for control host"); + return; + } + + auto method = this->bus.new_method_call(host.c_str(), + IPMI_PATH.c_str(), + IPMI_INTERFACE.c_str(), + "setAttention"); + auto reply = this->bus.call(method); + + if (reply.is_method_error()) + { + log<level::ERR>("Error in setting SMS attention"); + elog<InternalFailure>(); + } + log<level::INFO>("SMS Attention asserted"); + } +} + +// Called by specific implementations that provide commands +void Manager::execute(CommandHandler command) +{ + log<level::INFO>("Pushing cmd on to queue", + entry("COMMAND=%d", std::get<0>(command).first)); + + this->workQueue.emplace(command); + + // Alert host if this is only command in queue otherwise host will + // be notified of next message after processing the current one + if (this->workQueue.size() == 1) + { + this->checkQueueAndAlertHost(); + } + else + { + log<level::INFO>("Command in process, no attention"); + } + + return; +} + +} // namespace command +} // namespace host +} // namepsace phosphor diff --git a/host-cmd-manager.hpp b/host-cmd-manager.hpp new file mode 100644 index 0000000..0c938e5 --- /dev/null +++ b/host-cmd-manager.hpp @@ -0,0 +1,88 @@ +#pragma once + +#include <tuple> +#include <queue> +#include <sdbusplus/bus.hpp> +#include <timer.hpp> +#include <host-cmd-utils.hpp> + +namespace phosphor +{ +namespace host +{ +namespace command +{ + +/** @class + * @brief Manages commands that are to be sent to Host + */ +class Manager +{ + public: + Manager() = delete; + ~Manager() = default; + Manager(const Manager&) = delete; + Manager& operator=(const Manager&) = delete; + Manager(Manager&&) = delete; + Manager& operator=(Manager&&) = delete; + + /** @brief Constructs Manager object + * + * @param[in] bus - dbus handler + * @param[in] event - pointer to sd_event + */ + Manager(sdbusplus::bus::bus& bus, sd_event* event) : + bus(bus), + timer(event, std::bind(&Manager::hostTimeout, this)) + { + // Nothing to do here. + } + + /** @brief Extracts the next entry in the queue and returns + * Command and data part of it. + * + * @detail Also calls into the registered handlers so that they can now + * send the CommandComplete signal since the interface contract + * is that we emit this signal once the message has been + * passed to the host (which is required when calling this) + * + * Also, if the queue has more commands, then it will alert the + * host + */ + IpmiCmdData getNextCommand(); + + /** @brief Pushes the command onto the queue. + * + * @detail If the queue is empty, then it alerts the Host. If not, + * then it returns and the API documented above will handle + * the commands in Queue. + * + * @param[in] command - tuple of <IPMI command, data, callback> + */ + void execute(CommandHandler command); + + private: + /** @brief Check if anything in queue and alert host if so */ + void checkQueueAndAlertHost(); + + /** @brief Call back interface on message timeouts to host. + * + * @detail When this happens, a failure message would be sent + * to all the commands that are in the Queue and queue + * will be purged + */ + void hostTimeout(); + + /** @brief Reference to the dbus handler */ + sdbusplus::bus::bus& bus; + + /** @brief Queue to store the requested commands */ + std::queue<CommandHandler> workQueue{}; + + /** @brief Timer for commands to host */ + phosphor::ipmi::Timer timer; +}; + +} // namespace command +} // namespace host +} // namespace phosphor diff --git a/host-cmd-utils.hpp b/host-cmd-utils.hpp new file mode 100644 index 0000000..f564e2a --- /dev/null +++ b/host-cmd-utils.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include <unistd.h> +#include <tuple> + +namespace phosphor +{ +namespace host +{ +namespace command +{ + /** @detail After sending SMS_ATN to the Host, Host comes down and + * asks why an 'SMS_ATN` was sent. + * BMC then sends 'There is a Message to be Read` as reponse. + * Host then comes down asks for Message and the specified + * commands and data would go as data conforming to IPMI spec. + * + * Refer: 6.13.2 Send Message Command From System Interface + * in IPMI V2.0 spec. + */ + + /** @brief IPMI command */ + using IPMIcmd = uint8_t; + + /** @brief Data associated with command */ + using Data = uint8_t; + + /** @brief <IPMI command, Data> to be sent as payload when Host asks for + * the message that can be associated with the previous SMS_ATN + */ + using IpmiCmdData = std::pair<IPMIcmd, Data>; + + /** @detail Implementation specific callback function to be invoked + * conveying the status of the executed command. Specific + * implementations may then broadcast an agreed signal + */ + using CallBack = std::function<void(IpmiCmdData, bool)>; + + /** @detail Tuple encapsulating above 2 to enable using Manager by + * different implementations. Users of Manager will supply + * <Ipmi command, Data> along with the callback handler. + * Manager will invoke the handler onveying the status of + * the command. + */ + using CommandHandler = std::tuple<IpmiCmdData, CallBack>; + +} // namespace command +} // namespace host +} // namespace phosphor |