bmc-state-manager: Add support BMC Reboot Cause feature

Below is patch supporting this change.
https://gerrit.openbmc-project.xyz/c/openbmc/phosphor-dbus-interfaces/+/41359

Tested:
busctl get-property xyz.openbmc_project.State.BMC /xyz/openbmc_project/state/bmc0 xyz.openbmc_project.State.BMC LastRebootCause

When unplug/plug in BMC power cable then return as below:
s "xyz.openbmc_project.State.BMC.BMCResetCause.POR"

When executing reboot command then return as below:
s "xyz.openbmc_project.State.BMC.BMCResetCause.Watchdog"

Signed-off-by: Tim Lee <timlee660101@gmail.com>
Change-Id: I4e12f1df96cc1321beee0c4eae648d71e2d4a3b9
diff --git a/bmc_state_manager.cpp b/bmc_state_manager.cpp
index fb15f8f..2ee58bc 100644
--- a/bmc_state_manager.cpp
+++ b/bmc_state_manager.cpp
@@ -9,6 +9,9 @@
 #include <sdbusplus/exception.hpp>
 
 #include <cassert>
+#include <filesystem>
+#include <fstream>
+#include <iostream>
 
 namespace phosphor
 {
@@ -230,6 +233,15 @@
     return server::BMC::currentBMCState(value);
 }
 
+BMC::RebootCause BMC::lastRebootCause(RebootCause value)
+{
+    log<level::INFO>(
+        "Setting the RebootCause field",
+        entry("LAST_REBOOT_CAUSE=0x%s", convertForMessage(value).c_str()));
+
+    return server::BMC::lastRebootCause(value);
+}
+
 uint64_t BMC::lastRebootTime() const
 {
     using namespace std::chrono;
@@ -245,6 +257,45 @@
     return duration_cast<milliseconds>(rebootTime.time_since_epoch()).count();
 }
 
+void BMC::discoverLastRebootCause()
+{
+    uint64_t bootReason = 0;
+    std::ifstream file;
+    auto bootstatusPath = "/sys/class/watchdog/watchdog0/bootstatus";
+
+    file.exceptions(std::ifstream::failbit | std::ifstream::badbit |
+                    std::ifstream::eofbit);
+
+    try
+    {
+        file.open(bootstatusPath);
+        file >> bootReason;
+    }
+    catch (const std::exception& e)
+    {
+        auto rc = errno;
+        log<level::ERR>((std::string("Failed to read sysfs file "
+                                     "errno=") +
+                         std::to_string(rc) + " FILENAME=" + bootstatusPath)
+                            .c_str());
+    }
+
+    switch (bootReason)
+    {
+        case WDIOF_EXTERN1:
+            this->lastRebootCause(RebootCause::Watchdog);
+            break;
+        case WDIOF_CARDRESET:
+            this->lastRebootCause(RebootCause::POR);
+            break;
+        default:
+            this->lastRebootCause(RebootCause::Unknown);
+            break;
+    }
+
+    return;
+}
+
 } // namespace manager
 } // namespace state
 } // namespace phosphor
diff --git a/bmc_state_manager.hpp b/bmc_state_manager.hpp
index cf53795..4ea9784 100644
--- a/bmc_state_manager.hpp
+++ b/bmc_state_manager.hpp
@@ -2,6 +2,8 @@
 
 #include "xyz/openbmc_project/State/BMC/server.hpp"
 
+#include <linux/watchdog.h>
+
 #include <sdbusplus/bus.hpp>
 
 #include <functional>
@@ -43,6 +45,7 @@
     {
         subscribeToSystemdSignals();
         discoverInitialState();
+        discoverLastRebootCause();
         this->emit_object_added();
     };
 
@@ -62,6 +65,9 @@
      */
     uint64_t lastRebootTime() const override;
 
+    /** @brief Set value of LastRebootCause **/
+    RebootCause lastRebootCause(RebootCause value) override;
+
   private:
     /**
      * @brief discover the state of the bmc
@@ -94,6 +100,11 @@
 
     /** @brief Used to subscribe to dbus system state changes **/
     std::unique_ptr<sdbusplus::bus::match_t> stateSignal;
+
+    /**
+     * @brief discover the last reboot cause of the bmc
+     **/
+    void discoverLastRebootCause();
 };
 
 } // namespace manager