Updated BMCState when firmware update mode is set

Updated BMCState value to "UpdateInProgress" when
user sets the Firmware Update mode via IPMI command
and resetting it to "Ready" during exit Firmware update
mode.

This change will make sure correct setting of BMCState
when device is in update mode and same will be reflected
in GetDeviceId byte4 as per IPMI specification.

New value "UpdateInProgress" is added to dbus as part of
below change.
https://gerrit.openbmc-project.xyz/#/c/openbmc/phosphor-dbus-interfaces/+/26867/

Tested:
Set and reset the Firmware Update mode using OEM IPMI
commands and verified the get device ID response.
root@intel-obmc:~# ipmitool raw 8 0x26
 7f 64 a5 c9 af 00 b9 30
root@intel-obmc:~# ipmitool raw 8 0x27 0x7f 0x64 0xa5 0xc9 0xaf 0x00 0xb9 0x30

root@intel-obmc:~# ipmitool raw 6 1
 23 00 80 01 02 bf 57 01 00 7b 00 ca 0a 9a 62
root@intel-obmc:~# ipmitool raw 8 0x28

root@intel-obmc:~# ipmitool raw 6 1
 23 00 00 01 02 bf 57 01 00 7b 00 ca 0a 9a 62
root@intel-obmc:~#

Change-Id: Ie7e4a778cb80708a0a8ca239e9a24e82de1f9de2
Signed-off-by: AppaRao Puli <apparao.puli@linux.intel.com>
diff --git a/src/firmware-update.cpp b/src/firmware-update.cpp
index 6bd057a..d949a33 100644
--- a/src/firmware-update.cpp
+++ b/src/firmware-update.cpp
@@ -63,6 +63,13 @@
 
 #endif
 
+static constexpr const char *bmcStateIntf = "xyz.openbmc_project.State.BMC";
+static constexpr const char *bmcStatePath = "/xyz/openbmc_project/state/bmc0";
+static constexpr const char *bmcStateReady =
+    "xyz.openbmc_project.State.BMC.BMCState.Ready";
+static constexpr const char *bmcStateUpdateInProgress =
+    "xyz.openbmc_project.State.BMC.BMCState.UpdateInProgress";
+
 static constexpr const char *secondaryFitImageStartAddr = "22480000";
 static uint8_t getActiveBootImage(void);
 static void register_netfn_firmware_functions() __attribute__((constructor));
@@ -318,6 +325,51 @@
     return IPMI_CC_OK;
 }
 
+static bool getFirmwareUpdateMode()
+{
+    std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
+    try
+    {
+        auto service = ipmi::getService(*busp, bmcStateIntf, bmcStatePath);
+        ipmi::Value state = ipmi::getDbusProperty(
+            *busp, service, bmcStatePath, bmcStateIntf, "CurrentBMCState");
+        std::string bmcState = std::get<std::string>(state);
+        return (bmcState == bmcStateUpdateInProgress);
+    }
+    catch (const std::exception &e)
+    {
+        phosphor::logging::log<phosphor::logging::level::ERR>(
+            "Exception caught while getting BMC state.",
+            phosphor::logging::entry("EXCEPTION=%s", e.what()));
+        throw;
+    }
+}
+
+static void setFirmwareUpdateMode(const bool mode)
+{
+
+    std::string bmcState(bmcStateReady);
+    if (mode)
+    {
+        bmcState = bmcStateUpdateInProgress;
+    }
+
+    std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
+    try
+    {
+        auto service = ipmi::getService(*busp, bmcStateIntf, bmcStatePath);
+        ipmi::setDbusProperty(*busp, service, bmcStatePath, bmcStateIntf,
+                              "CurrentBMCState", bmcState);
+    }
+    catch (const std::exception &e)
+    {
+        phosphor::logging::log<phosphor::logging::level::ERR>(
+            "Exception caught while setting BMC state.",
+            phosphor::logging::entry("EXCEPTION=%s", e.what()));
+        throw;
+    }
+}
+
 /** @brief Set Firmware Update Mode
  *
  *  This function sets BMC into firmware update mode
@@ -355,17 +407,20 @@
         }
     }
 
-    if (fw_update_status.state() != fw_update_status_cache::FW_STATE_IDLE
-        // TODO: Allowing FW_STATE_INIT here to let image activation available
-        // without being in FW_STATE_IDLE, need to fix/adjust the state machine
-        // to match xyz.openbmc_project.Software.BMC.Updater service activation
-        // mechanism at finer grain
-        && fw_update_status.state() != fw_update_status_cache::FW_STATE_INIT)
+    try
     {
-        phosphor::logging::log<phosphor::logging::level::INFO>(
-            "Already firmware update is in progress.");
-        return ipmi::responseBusy();
+        if (getFirmwareUpdateMode())
+        {
+            phosphor::logging::log<phosphor::logging::level::INFO>(
+                "Already firmware update is in progress.");
+            return ipmi::responseBusy();
+        }
     }
+    catch (const std::exception &e)
+    {
+        return ipmi::responseUnspecifiedError();
+    }
+
     // FIXME? c++ doesn't off an option for exclusive file creation
     FILE *fp = fopen(FIRMWARE_BUFFER_FILE, "wx");
     if (!fp)
@@ -376,6 +431,16 @@
     }
     fclose(fp);
 
+    try
+    {
+        setFirmwareUpdateMode(true);
+    }
+    catch (const std::exception &e)
+    {
+        unlink(FIRMWARE_BUFFER_FILE);
+        return ipmi::responseUnspecifiedError();
+    }
+
     return ipmi::responseSuccess();
 }
 
@@ -410,6 +475,16 @@
             break;
     }
     fw_update_status.firmwareUpdateAbortState();
+
+    try
+    {
+        setFirmwareUpdateMode(false);
+    }
+    catch (const std::exception &e)
+    {
+        return ipmi::responseUnspecifiedError();
+    }
+
     return ipmi::responseSuccess();
 }