Add MTM keep alive command support

MTM keep alive command is used to extend the Manufacturing
mode timeout by 15 minutes, from the time command is executed.
This is used to keep the manufacturing mode alive, till all
the desired activities are done.

Tested:
1. Issued ipmitool raw 0x30 0xB5 0 0x49 0x4e 0x54 0x45 0x4c
and able to see timer extended for 15 min, after which
manufacturing mode got expired
2. Verified timer is sending 0xC1, when BMC is not in
manufacturing mode.

Change-Id: I2ae309d9030c672bb930c317aef357a97327ba80
Signed-off-by: Richard Marian Thomaiyar <richard.marian.thomaiyar@linux.intel.com>
diff --git a/include/manufacturingcommands.hpp b/include/manufacturingcommands.hpp
index e015fd2..c36fcb0 100644
--- a/include/manufacturingcommands.hpp
+++ b/include/manufacturingcommands.hpp
@@ -56,6 +56,13 @@
 static constexpr const char* ledStateStr =
     "xyz.openbmc_project.Led.Physical.Action."; // Comes with postfix Off/On
 
+static constexpr const char* specialModeService =
+    "xyz.openbmc_project.SpecialMode";
+static constexpr const char* specialModeObjPath =
+    "/xyz/openbmc_project/security/special_mode";
+static constexpr const char* specialModeIntf =
+    "xyz.openbmc_project.Security.SpecialMode";
+
 /** @enum MtmLvl
 .*
  *  Manufacturing command access levels
@@ -243,10 +250,8 @@
         if (mtmMode != MtmLvl::mtmExpired)
         {
             ipmi::Value mode;
-            if (getProperty("xyz.openbmc_project.SpecialMode",
-                            "/xyz/openbmc_project/security/special_mode",
-                            "xyz.openbmc_project.Security.SpecialMode",
-                            "SpecialMode", &mode) != 0)
+            if (getProperty(specialModeService, specialModeObjPath,
+                            specialModeIntf, "SpecialMode", &mode) != 0)
             {
                 mtmMode = MtmLvl::mtmExpired;
                 return mtmMode;
diff --git a/include/oemcommands.hpp b/include/oemcommands.hpp
index fa23a98..9ce2e55 100644
--- a/include/oemcommands.hpp
+++ b/include/oemcommands.hpp
@@ -44,6 +44,7 @@
     cmdGetProcessorErrConfig = 0x9A,
     cmdSetProcessorErrConfig = 0x9B,
     cmdGetLEDStatus = 0xB0,
+    cmdMtmKeepAlive = 0xB5,
     cmdGetNmiStatus = 0xE5,
     cmdSetNmiStatus = 0xED,
 };
diff --git a/src/manufacturingcommands.cpp b/src/manufacturingcommands.cpp
index a85b872..483e077 100644
--- a/src/manufacturingcommands.cpp
+++ b/src/manufacturingcommands.cpp
@@ -41,6 +41,22 @@
     "org.freedesktop.systemd1.Manager";
 const static constexpr char* pidControlService = "phosphor-pid-control.service";
 
+static inline Cc resetMtmTimer(boost::asio::yield_context yield)
+{
+    auto sdbusp = getSdBus();
+    boost::system::error_code ec;
+    sdbusp->yield_method_call<>(yield, ec, specialModeService,
+                                specialModeObjPath, specialModeIntf,
+                                "ResetTimer");
+    if (ec)
+    {
+        phosphor::logging::log<phosphor::logging::level::ERR>(
+            "Failed to reset the manufacturing mode timer");
+        return ccUnspecifiedError;
+    }
+    return ccSuccess;
+}
+
 int getGpioPathForSmSignal(const SmSignalGet signal, std::string& path)
 {
     switch (signal)
@@ -558,6 +574,22 @@
     return retCode;
 }
 
+ipmi::RspType<> mtmKeepAlive(boost::asio::yield_context yield, uint8_t reserved,
+                             const std::array<char, 5>& intentionalSignature)
+{
+    // Allow MTM keep alive command only in manfacturing mode.
+    if (mtm.getAccessLvl() != MtmLvl::mtmAvailable)
+    {
+        return ipmi::responseInvalidCommand();
+    }
+    constexpr std::array<char, 5> signatureOk = {'I', 'N', 'T', 'E', 'L'};
+    if (intentionalSignature != signatureOk || reserved != 0)
+    {
+        return ipmi::responseInvalidFieldRequest();
+    }
+    return ipmi::response(resetMtmTimer(yield));
+}
+
 } // namespace ipmi
 
 void register_mtm_commands() __attribute__((constructor));
@@ -574,5 +606,10 @@
         static_cast<ipmi_cmd_t>(IPMINetFnIntelOemGeneralCmds::SetSmSignal),
         NULL, ipmi::ipmi_app_mtm_set_signal, PRIVILEGE_USER);
 
+    ipmi::registerHandler(
+        ipmi::prioOemBase, ipmi::netFnOemOne,
+        static_cast<ipmi::Cmd>(IPMINetfnIntelOEMGeneralCmd::cmdMtmKeepAlive),
+        ipmi::Privilege::Admin, ipmi::mtmKeepAlive);
+
     return;
 }