Add support to send host bound commands for OpenPower machines

Ipmid launches a D-Bus service that would enable REST users to
send commands to Host. This commit puts the support for using
that service to send commands that are applicable to OpenPower
implementations only.

Change-Id: I68dcb3e742145b85a719fa3045367402aa1ed19b
Signed-off-by: Vishwanatha Subbanna <vishwa@linux.vnet.ibm.com>
diff --git a/Makefile.am b/Makefile.am
index 3ebcdb8..aa44203 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,5 +1,17 @@
 liboemhandlerdir = ${libdir}/ipmid-providers
 liboemhandler_LTLIBRARIES = liboemhandler.la
-liboemhandler_la_SOURCES = oemhandler.cpp
-liboemhandler_la_LDFLAGS = $(SYSTEMD_LIBS) -version-info 0:0:0 -shared
-liboemhandler_la_CXXFLAGS = $(SYSTEMD_CFLAGS) $(AM_CXXFLAGS)
+
+liboemhandler_la_SOURCES = oemhandler.cpp \
+                           host-interface.cpp
+
+liboemhandler_la_LDFLAGS = $(SYSTEMD_LIBS)\
+                           $(SDBUSPLUS_LIBS) \
+                           $(OPENPOWER_DBUS_INTERFACES_LIBS) \
+                           $(PHOSPHOR_LOGGING_LIBS) \
+                           -version-info 0:0:0 -shared
+
+liboemhandler_la_CXXFLAGS = $(SYSTEMD_CFLAGS) \
+                            $(SDBUSPLUS_CFLAGS) \
+                            $(OPENPOWER_DBUS_INTERFACES_CFLAGS) \
+                            $(PHOSPHOR_LOGGING_CFLAGS) \
+                            $(AM_CXXFLAGS)
diff --git a/configure.ac b/configure.ac
index cdd0292..2198682 100644
--- a/configure.ac
+++ b/configure.ac
@@ -17,6 +17,8 @@
 # Checks for header files.
 AC_CHECK_HEADER(systemd/sd-bus.h, ,[AC_MSG_ERROR([Could not find systemd/sd-bus.h...systemd development package required])])
 AC_CHECK_HEADER(host-ipmid/ipmid-api.h, ,[AC_MSG_ERROR([Could not find host-ipmid/ipmid-api.h...host-ipmid package required])])
+PKG_CHECK_MODULES([OPENPOWER_DBUS_INTERFACES], [openpower-dbus-interfaces],,
+                  [AC_MSG_ERROR([Could not find openpower-dbus-interfaces...openbmc/openpower-dbus-interfaces package required])])
 
 # Checks for typedefs, structures, and compiler characteristics.
 AX_CXX_COMPILE_STDCXX(14, [noext])
@@ -47,6 +49,18 @@
     AC_SUBST([OESDK_TESTCASE_FLAGS], [$testcase_flags])
 )
 
+# Host object name in the D-Bus
+AC_ARG_VAR(HOST_NAME, [The Host name in the object path])
+AS_IF([test "x$HOST_NAME" == "x"],
+      [HOST_NAME="host"])
+AC_DEFINE_UNQUOTED([HOST_NAME], ["$HOST_NAME"], [The Host name in the object path])
+
+# Service dbus object manager
+AC_ARG_VAR(CONTROL_HOST_OBJ_MGR, [The Control Host D-Bus Object Manager])
+AS_IF([test "x$CONTROL_HOST_OBJ_MGR" == "x"],
+      [CONTROL_HOST_OBJ_MGR="/org/open_power/control"])
+AC_DEFINE_UNQUOTED([CONTROL_HOST_OBJ_MGR], ["$CONTROL_HOST_OBJ_MGR"], [The Control Host D-Bus Object Manager])
+
 # Create configured output.
 AC_CONFIG_FILES([Makefile])
 AC_OUTPUT
diff --git a/host-interface.cpp b/host-interface.cpp
new file mode 100644
index 0000000..f32db3f
--- /dev/null
+++ b/host-interface.cpp
@@ -0,0 +1,69 @@
+#include <chrono>
+#include <functional>
+#include <oemhandler.hpp>
+#include <host-ipmid/ipmid-host-cmd.hpp>
+#include <host-ipmid/ipmid-host-cmd-utils.hpp>
+#include <phosphor-logging/log.hpp>
+#include <config.h>
+#include <host-interface.hpp>
+namespace open_power
+{
+namespace host
+{
+namespace command
+{
+
+// IPMI command
+// https://github.com/openbmc/openbmc/issues/2082 for handling
+// Non-OEM commands that need to send SMS_ATN
+using OEMCmd = uint8_t;
+
+// Map of IPMI OEM command to its equivalent interface command.
+// This is needed when invoking the callback handler to indicate
+// the status of the executed command.
+static const std::map<OEMCmd, Host::Command> intfCommand = {
+    {
+        IPMI_CMD_OCC_RESET,
+            Base::Host::Command::OCCReset
+    }
+};
+
+// Called at user request
+void Host::execute(Base::Host::Command command,
+        sdbusplus::message::variant<uint8_t> data)
+{
+    using namespace phosphor::logging;
+
+    log<level::INFO>("Pushing cmd on to queue",
+            entry("CONTROL_HOST_CMD=%s",
+                  convertForMessage(command)));
+
+    // If the command is OCCReset, then all we need is just sensor ID
+    // This is the only command that is being used now.
+    if (command == Base::Host::Command::OCCReset)
+    {
+        auto sensorID = sdbusplus::message::variant_ns::get<uint8_t>(data);
+
+        auto cmd = std::make_tuple(std::make_pair(IPMI_CMD_OCC_RESET, sensorID),
+                std::bind(&Host::commandStatusHandler,
+                    this, std::placeholders::_1,
+                    std::placeholders::_2));
+
+        return ipmid_send_cmd_to_host(std::move(cmd));
+    }
+    return;
+}
+
+// Called into by Command Manager
+void Host::commandStatusHandler(IpmiCmdData cmd, bool status)
+{
+    // Need to convert <cmd> to the equivalent one mentioned in spec
+    auto value = status ? Result::Success : Result::Failure;
+
+    // Fire a signal
+    this->commandComplete(intfCommand.at(std::get<0>(cmd)), value);
+}
+
+} // namespace command
+} // namespace host
+} // namepsace open_power
diff --git a/host-interface.hpp b/host-interface.hpp
new file mode 100644
index 0000000..4b92e18
--- /dev/null
+++ b/host-interface.hpp
@@ -0,0 +1,65 @@
+#pragma once
+
+#include <sdbusplus/bus.hpp>
+#include <host-ipmid/ipmid-host-cmd-utils.hpp>
+#include <org/open_power/Control/Host/server.hpp>
+namespace open_power
+{
+namespace host
+{
+namespace command
+{
+
+namespace Base = sdbusplus::org::open_power::Control::server;
+using IpmiCmdData = phosphor::host::command::IpmiCmdData;
+
+/** @class Host
+ *  @brief OpenBMC control host interface implementation.
+ *  @details A concrete implementation for org.open_power.Control.Host
+ *  DBus API.
+ */
+class Host : public sdbusplus::server::object::object<Base::Host>
+{
+    public:
+        /** @brief Constructs Host Control Interface
+         *
+         *  @param[in] bus     - The Dbus bus object
+         *  @param[in] objPath - The Dbus object path
+         */
+        Host(sdbusplus::bus::bus& bus,
+             const char* objPath) :
+             sdbusplus::server::object::object<Base::Host>(bus, objPath),
+             bus(bus)
+        {
+            // Nothing to do
+        }
+
+        /** @brief Sends input command to host
+         *         Note that the command will be queued in a FIFO if
+         *         other commands to the host have yet to be run
+         *
+         *  @param[in] command - Input command to execute
+         *  @param[in] data    - Data associated with the command
+         */
+        void execute(Command command,
+                sdbusplus::message::variant<uint8_t> data) override;
+
+    private:
+        /** @brief sdbusplus DBus bus connection. */
+        sdbusplus::bus::bus& bus;
+
+        /** @brief  Callback function to be invoked by command manager
+         *
+         *  @detail Conveys the status of the last Host bound command.
+         *          Depending on the status,  a CommandComplete or
+         *          CommandFailure signal would be sent
+         *
+         *  @param[in] cmd    - IPMI command and data sent to Host
+         *  @param[in] status - Success or Failure
+         */
+        void commandStatusHandler(IpmiCmdData cmd, bool status);
+};
+
+} // namespace command
+} // namespace host
+} // namespace phosphor
diff --git a/oemhandler.cpp b/oemhandler.cpp
index 91bf2b7..aba86e3 100644
--- a/oemhandler.cpp
+++ b/oemhandler.cpp
@@ -1,10 +1,15 @@
 #include "oemhandler.hpp"
+#include "config.h"
 #include <host-ipmid/ipmid-api.h>
+#include <host-ipmid/ipmid-host-cmd.hpp>
 #include <fstream>
 #include <stdio.h>
 #include <string.h>
-#include <systemd/sd-bus.h>
 #include <endian.h>
+#include <functional>
+#include <systemd/sd-bus.h>
+#include <sdbusplus/bus.hpp>
+#include <host-interface.hpp>
 
 void register_netfn_oem_partial_esel() __attribute__((constructor));
 
@@ -136,6 +141,14 @@
     return ipmi_rc;
 }
 
+namespace {
+// Storage to keep the object alive during process life
+std::unique_ptr<open_power::host::command::Host> opHost
+        __attribute__((init_priority(101)));
+std::unique_ptr<sdbusplus::server::manager::manager> objManager
+        __attribute__((init_priority(101)));
+}
+
 void register_netfn_oem_partial_esel()
 {
     printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_OEM, IPMI_CMD_PESEL);
@@ -146,5 +159,19 @@
     ipmi_register_callback(NETFUN_OEM, IPMI_CMD_PREP_FW_UPDATE, NULL, ipmi_ibm_oem_prep_fw_update,
                            SYSTEM_INTERFACE);
 
+    // Create new object on the bus
+    auto objPath = std::string{CONTROL_HOST_OBJ_MGR} + '/' + HOST_NAME + '0';
+
+    // Add sdbusplus ObjectManager.
+    auto& sdbusPlusHandler = ipmid_get_sdbus_plus_handler();
+    objManager = std::make_unique<sdbusplus::server::manager::manager>(
+                    *sdbusPlusHandler,
+                    CONTROL_HOST_OBJ_MGR);
+
+    opHost = std::make_unique<open_power::host::command::Host>(
+                        *sdbusPlusHandler, objPath.c_str());
+
+    // Service for this is provided by phosphor layer systemcmdintf
+    // and this will be as part of that.
     return;
 }
diff --git a/oemhandler.hpp b/oemhandler.hpp
index 1c8a97e..539c6d3 100644
--- a/oemhandler.hpp
+++ b/oemhandler.hpp
@@ -10,6 +10,7 @@
 {
     IPMI_CMD_PREP_FW_UPDATE = 0x10,
     IPMI_CMD_PESEL = 0xF0,
+    IPMI_CMD_OCC_RESET = 0x0E,
 };