PEL: Update bmc & platform dump status in SRC section

With every PEL creation, the status of bmc and platform dumps -
Hardware and Hypervisor would be checked and status bits in SRC
section updated accordingly.

Change-Id: I3ec7626611cf330f2ce235a97ee3046c0d32b6ab
Signed-off-by: Sumit Kumar <sumit_kumar@in.ibm.com>
diff --git a/extensions/openpower-pels/data_interface.cpp b/extensions/openpower-pels/data_interface.cpp
index f8b701f..393c2fe 100644
--- a/extensions/openpower-pels/data_interface.cpp
+++ b/extensions/openpower-pels/data_interface.cpp
@@ -22,6 +22,7 @@
 #include <fmt/format.h>
 
 #include <fstream>
+#include <iterator>
 #include <phosphor-logging/log.hpp>
 #include <xyz/openbmc_project/State/Boot/Progress/server.hpp>
 
@@ -80,6 +81,8 @@
     "xyz.openbmc_project.State.Decorator.OperationalStatus";
 constexpr auto logSetting = "xyz.openbmc_project.Logging.Settings";
 constexpr auto association = "xyz.openbmc_project.Association.Definitions";
+constexpr auto dumpEntry = "xyz.openbmc_project.Dump.Entry";
+constexpr auto dumpProgress = "xyz.openbmc_project.Common.Progress";
 } // namespace interface
 
 using namespace sdbusplus::xyz::openbmc_project::State::Boot::server;
@@ -617,5 +620,72 @@
     return ret;
 }
 
+std::vector<bool>
+    DataInterface::checkDumpStatus(const std::vector<std::string>& type) const
+{
+    DBusSubTree subtree;
+    std::vector<bool> result(type.size(), false);
+
+    // Query GetSubTree for the availability of dump interface
+    auto method = _bus.new_method_call(service_name::objectMapper,
+                                       object_path::objectMapper,
+                                       interface::objectMapper, "GetSubTree");
+    method.append(std::string{"/"}, 0,
+                  std::vector<std::string>{interface::dumpEntry});
+    auto reply = _bus.call(method);
+
+    reply.read(subtree);
+
+    if (subtree.empty())
+    {
+        return result;
+    }
+
+    std::vector<bool>::iterator itDumpStatus = result.begin();
+    uint8_t count = 0;
+    for (const auto& [path, serviceInfo] : subtree)
+    {
+        const auto& service = serviceInfo.begin()->first;
+        // Check for dump type on the object path
+        for (const auto& it : type)
+        {
+            if (path.find(it) != std::string::npos)
+            {
+                DBusValue value, progress;
+
+                // If dump type status is already available go for next path
+                if (*itDumpStatus)
+                {
+                    break;
+                }
+
+                // Check for valid dump to be available if following
+                // conditions are met for the dump entry path -
+                // Offloaded == false and Status == Completed
+                getProperty(service, path, interface::dumpEntry, "Offloaded",
+                            value);
+                getProperty(service, path, interface::dumpProgress, "Status",
+                            progress);
+                auto offload = std::get<bool>(value);
+                auto status = std::get<std::string>(progress);
+                if (!offload && (status.find("Completed") != std::string::npos))
+                {
+                    *itDumpStatus = true;
+                    count++;
+                    if (count >= type.size())
+                    {
+                        return result;
+                    }
+                    break;
+                }
+            }
+            itDumpStatus++;
+        }
+        itDumpStatus = result.begin();
+    }
+
+    return result;
+}
+
 } // namespace pels
 } // namespace openpower
diff --git a/extensions/openpower-pels/data_interface.hpp b/extensions/openpower-pels/data_interface.hpp
index 89b09ed..482b4f1 100644
--- a/extensions/openpower-pels/data_interface.hpp
+++ b/extensions/openpower-pels/data_interface.hpp
@@ -327,6 +327,14 @@
     static std::pair<std::string, std::string>
         extractConnectorFromLocCode(const std::string& locationCode);
 
+    /**
+     * @brief Returns the dump status
+     *
+     * @return bool dump status
+     */
+    virtual std::vector<bool>
+        checkDumpStatus(const std::vector<std::string>& type) const = 0;
+
   protected:
     /**
      * @brief Sets the host on/off state and runs any
@@ -600,6 +608,16 @@
      */
     bool getQuiesceOnError() const override;
 
+    /**
+     * @brief Returns the dump status
+     *
+     * @param[in] type - The dump type to check for
+     *
+     * @return bool dump status
+     */
+    std::vector<bool>
+        checkDumpStatus(const std::vector<std::string>& type) const override;
+
   private:
     /**
      * @brief Reads the BMC firmware version string and puts it into
diff --git a/extensions/openpower-pels/src.cpp b/extensions/openpower-pels/src.cpp
index 6c6a2d4..a9f1b7f 100644
--- a/extensions/openpower-pels/src.cpp
+++ b/extensions/openpower-pels/src.cpp
@@ -348,11 +348,12 @@
     //   M: Partition dump status = 0
     //   I: System boot state = TODO
     //   G: Partition Boot type = 0
-    //   V: BMC dump status = TODO
+    //   V: BMC dump status
     //   E: Platform boot mode = 0 (side = temporary, speed = fast)
-    //   P: Platform dump status = TODO
+    //   P: Platform dump status
     //  FF: SRC format, set below
 
+    setDumpStatus(dataIface);
     setBMCFormat();
     setBMCPosition();
     setMotherboardCCIN(dataIface);
@@ -1403,5 +1404,28 @@
     return mrus;
 }
 
+void SRC::setDumpStatus(const DataInterfaceBase& dataIface)
+{
+    std::vector<bool> dumpStatus{false, false, false};
+
+    try
+    {
+        std::vector<std::string> dumpType = {"bmc/entry", "resource/entry",
+                                             "system/entry"};
+        dumpStatus = dataIface.checkDumpStatus(dumpType);
+
+        // For bmc      - set bit 0 of nibble [4-7] bits of byte-1 SP dump
+        // For resource - set bit 2 of nibble [4-7] bits of byte-2 Hypervisor
+        // For system   - set bit 1 of nibble [4-7] bits of byte-2 HW dump
+        _hexData[0] |= ((dumpStatus[0] << 19) | (dumpStatus[1] << 9) |
+                        (dumpStatus[2] << 10));
+    }
+    catch (const std::exception& e)
+    {
+        // Exception - may be no dump interface on dbus or getProperty
+        // failed
+    }
+}
+
 } // namespace pels
 } // namespace openpower
diff --git a/extensions/openpower-pels/src.hpp b/extensions/openpower-pels/src.hpp
index 5363864..f0b151e 100644
--- a/extensions/openpower-pels/src.hpp
+++ b/extensions/openpower-pels/src.hpp
@@ -522,6 +522,13 @@
         getMRUsFromJSON(const nlohmann::json& mruJSON);
 
     /**
+     * @brief Sets the dump status
+     *
+     * @param[in] dataIface - The DataInterface object
+     */
+    void setDumpStatus(const DataInterfaceBase& dataIface);
+
+    /**
      * @brief The SRC version field
      */
     uint8_t _version;
diff --git a/test/openpower-pels/mocks.hpp b/test/openpower-pels/mocks.hpp
index 11baa4b..80b4a5e 100644
--- a/test/openpower-pels/mocks.hpp
+++ b/test/openpower-pels/mocks.hpp
@@ -47,6 +47,8 @@
     MOCK_METHOD(bool, getQuiesceOnError, (), (const override));
     MOCK_METHOD(void, setCriticalAssociation, (const std::string&),
                 (const override));
+    MOCK_METHOD(std::vector<bool>, checkDumpStatus,
+                (const std::vector<std::string>&), (const override));
 
     void changeHostState(bool newState)
     {
diff --git a/test/openpower-pels/pel_manager_test.cpp b/test/openpower-pels/pel_manager_test.cpp
index 8221fab..bb382b7 100644
--- a/test/openpower-pels/pel_manager_test.cpp
+++ b/test/openpower-pels/pel_manager_test.cpp
@@ -273,6 +273,14 @@
     std::unique_ptr<DataInterfaceBase> dataIface =
         std::make_unique<MockDataInterface>();
 
+    MockDataInterface* mockIface =
+        reinterpret_cast<MockDataInterface*>(dataIface.get());
+
+    std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
+                                      "system/entry"};
+    EXPECT_CALL(*mockIface, checkDumpStatus(dumpType))
+        .WillRepeatedly(Return(std::vector<bool>{false, false, false}));
+
     openpower::pels::Manager manager{
         logManager, std::move(dataIface),
         std::bind(std::mem_fn(&TestLogger::log), &logger, std::placeholders::_1,
@@ -874,6 +882,11 @@
     MockDataInterface* mockIface =
         reinterpret_cast<MockDataInterface*>(dataIface.get());
 
+    std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
+                                      "system/entry"};
+    EXPECT_CALL(*mockIface, checkDumpStatus(dumpType))
+        .WillRepeatedly(Return(std::vector<bool>{false, false, false}));
+
     openpower::pels::Manager manager{
         logManager, std::move(dataIface),
         std::bind(std::mem_fn(&TestLogger::log), &logger, std::placeholders::_1,
diff --git a/test/openpower-pels/pel_test.cpp b/test/openpower-pels/pel_test.cpp
index 318a218..f980c9d 100644
--- a/test/openpower-pels/pel_test.cpp
+++ b/test/openpower-pels/pel_test.cpp
@@ -167,6 +167,11 @@
     NiceMock<MockDataInterface> dataIface;
     PelFFDC ffdc;
 
+    std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
+                                      "system/entry"};
+    EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
+        .WillRepeatedly(Return(std::vector<bool>{false, false, false}));
+
     PEL pel{regEntry, 42,   timestamp, phosphor::logging::Entry::Level::Error,
             ad,       ffdc, dataIface};
 
@@ -242,6 +247,11 @@
     AdditionalData ad{data};
     NiceMock<MockDataInterface> dataIface;
 
+    std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
+                                      "system/entry"};
+    EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
+        .WillOnce(Return(std::vector<bool>{false, false, false}));
+
     PEL pel{regEntry, 42,   timestamp, phosphor::logging::Entry::Level::Error,
             ad,       ffdc, dataIface};
 
@@ -762,6 +772,11 @@
     ffdc.emplace_back(std::move(getCustomFFDC(dir, customData)));
     ffdc.emplace_back(std::move(getCustomFFDC(dir, hugeCustomData)));
 
+    std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
+                                      "system/entry"};
+    EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
+        .WillOnce(Return(std::vector<bool>{false, false, false}));
+
     PEL pel{regEntry, 42,   timestamp, phosphor::logging::Entry::Level::Error,
             ad,       ffdc, dataIface};
 
@@ -849,6 +864,11 @@
         .WillOnce(DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"),
                         SetArgReferee<3>("123456789ABC")));
 
+    std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
+                                      "system/entry"};
+    EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
+        .WillRepeatedly(Return(std::vector<bool>{false, false, false}));
+
     auto dataPath = getPELReadOnlyDataPath();
     std::ofstream file{dataPath / "systemA_dev_callouts.json"};
     file << calloutJSON;
@@ -987,6 +1007,11 @@
         .WillOnce(DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"),
                         SetArgReferee<3>("123456789ABC")));
 
+    std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
+                                      "system/entry"};
+    EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
+        .WillOnce(Return(std::vector<bool>{false, false, false}));
+
     message::Entry regEntry;
     regEntry.name = "test";
     regEntry.subsystem = 5;
diff --git a/test/openpower-pels/src_test.cpp b/test/openpower-pels/src_test.cpp
index af8d04e..a9e52d2 100644
--- a/test/openpower-pels/src_test.cpp
+++ b/test/openpower-pels/src_test.cpp
@@ -206,6 +206,11 @@
 
     EXPECT_CALL(dataIface, getMotherboardCCIN).WillOnce(Return("ABCD"));
 
+    std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
+                                      "system/entry"};
+    EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
+        .WillOnce(Return(std::vector<bool>{false, false, false}));
+
     SRC src{entry, ad, dataIface};
 
     EXPECT_TRUE(src.valid());
@@ -269,6 +274,11 @@
     AdditionalData ad{adData};
     NiceMock<MockDataInterface> dataIface;
 
+    std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
+                                      "system/entry"};
+    EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
+        .WillOnce(Return(std::vector<bool>{false, false, false}));
+
     SRC src{entry, ad, dataIface};
 
     EXPECT_TRUE(src.valid());
@@ -289,6 +299,11 @@
     AdditionalData ad{adData};
     NiceMock<MockDataInterface> dataIface;
 
+    std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
+                                      "system/entry"};
+    EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
+        .WillRepeatedly(Return(std::vector<bool>{false, false, false}));
+
     // First it isn't a number, then it is too long,
     // then it is empty.
     EXPECT_CALL(dataIface, getMotherboardCCIN)
@@ -331,6 +346,11 @@
     AdditionalData ad{adData};
     NiceMock<MockDataInterface> dataIface;
 
+    std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
+                                      "system/entry"};
+    EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
+        .WillOnce(Return(std::vector<bool>{false, false, false}));
+
     SRC src{*entry, ad, dataIface};
     EXPECT_TRUE(src.valid());
 
@@ -362,6 +382,11 @@
         .WillOnce(DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"),
                         SetArgReferee<3>("123456789ABC")));
 
+    std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
+                                      "system/entry"};
+    EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
+        .WillOnce(Return(std::vector<bool>{false, false, false}));
+
     SRC src{entry, ad, dataIface};
     EXPECT_TRUE(src.valid());
 
@@ -415,6 +440,11 @@
         .Times(1)
         .WillOnce(InvokeWithoutArgs(func));
 
+    std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
+                                      "system/entry"};
+    EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
+        .WillOnce(Return(std::vector<bool>{false, false, false}));
+
     EXPECT_CALL(dataIface, getHWCalloutFields(_, _, _, _)).Times(0);
 
     SRC src{entry, ad, dataIface};
@@ -457,6 +487,11 @@
         .Times(1)
         .WillOnce(InvokeWithoutArgs(func));
 
+    std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
+                                      "system/entry"};
+    EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
+        .WillOnce(Return(std::vector<bool>{false, false, false}));
+
     SRC src{entry, ad, dataIface};
     EXPECT_TRUE(src.valid());
     ASSERT_TRUE(src.callouts());
@@ -548,6 +583,11 @@
 
         EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names));
 
+        std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
+                                          "system/entry"};
+        EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
+            .WillOnce(Return(std::vector<bool>{false, false, false}));
+
         SRC src{entry, ad, dataIface};
 
         auto& callouts = src.callouts()->callouts();
@@ -585,6 +625,11 @@
         EXPECT_CALL(dataIface, expandLocationCode).WillOnce(Return("P0-C8"));
         EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names));
 
+        std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
+                                          "system/entry"};
+        EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
+            .WillOnce(Return(std::vector<bool>{false, false, false}));
+
         SRC src{entry, ad, dataIface};
 
         auto& callouts = src.callouts()->callouts();
@@ -620,6 +665,11 @@
         NiceMock<MockDataInterface> dataIface;
         std::vector<std::string> names{"systemC"};
 
+        std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
+                                          "system/entry"};
+        EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
+            .WillOnce(Return(std::vector<bool>{false, false, false}));
+
         EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names));
 
         EXPECT_CALL(dataIface, expandLocationCode("P0-C8", 0))
@@ -716,6 +766,11 @@
         NiceMock<MockDataInterface> dataIface;
         std::vector<std::string> names{"systemA"};
 
+        std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
+                                          "system/entry"};
+        EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
+            .WillOnce(Return(std::vector<bool>{false, false, false}));
+
         EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names));
 
         EXPECT_CALL(dataIface, getLocationCode("motherboard"))
@@ -769,6 +824,11 @@
         NiceMock<MockDataInterface> dataIface;
         std::vector<std::string> names{"systemA"};
 
+        std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
+                                          "system/entry"};
+        EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
+            .WillOnce(Return(std::vector<bool>{false, false, false}));
+
         EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names));
 
         SRC src{entry, ad, dataIface};
@@ -832,6 +892,11 @@
     NiceMock<MockDataInterface> dataIface;
     std::vector<std::string> names{"systemA"};
 
+    std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
+                                      "system/entry"};
+    EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
+        .WillRepeatedly(Return(std::vector<bool>{false, false, false}));
+
     EXPECT_CALL(dataIface, getSystemNames)
         .Times(5)
         .WillRepeatedly(Return(names));
@@ -1042,6 +1107,11 @@
     AdditionalData ad;
     NiceMock<MockDataInterface> dataIface;
 
+    std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
+                                      "system/entry"};
+    EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
+        .WillOnce(Return(std::vector<bool>{false, false, false}));
+
     // Callout 0 mock calls
     {
         EXPECT_CALL(dataIface, expandLocationCode("P0-C1", 0))
@@ -1207,6 +1277,11 @@
     AdditionalData ad;
     NiceMock<MockDataInterface> dataIface;
 
+    std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
+                                      "system/entry"};
+    EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
+        .WillRepeatedly(Return(std::vector<bool>{false, false, false}));
+
     // Callout 0 mock calls
     // Expand location code will fail, so the unexpanded location
     // code should show up in the callout instead.
@@ -1287,6 +1362,11 @@
     AdditionalData ad{adData};
     NiceMock<MockDataInterface> dataIface;
 
+    std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
+                                      "system/entry"};
+    EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
+        .WillOnce(Return(std::vector<bool>{false, false, false}));
+
     EXPECT_CALL(dataIface, getLocationCode("motherboard"))
         .WillOnce(Return("UTMS-P1"));
 
@@ -1307,3 +1387,62 @@
     EXPECT_EQ(callout->locationCode(), "UTMS-P1");
     EXPECT_EQ(callout->priority(), 'M');
 }
+
+// Test for bmc & platform dump status bits
+TEST_F(SRCTest, DumpStatusBitsCheck)
+{
+    message::Entry entry;
+    entry.src.type = 0xBD;
+    entry.src.reasonCode = 0xABCD;
+    entry.subsystem = 0x42;
+    entry.src.powerFault = false;
+
+    AdditionalData ad;
+    NiceMock<MockDataInterface> dataIface;
+    std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
+                                      "system/entry"};
+
+    {
+        EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
+            .WillOnce(Return(std::vector<bool>{true, false, false}));
+
+        SRC src{entry, ad, dataIface};
+        EXPECT_TRUE(src.valid());
+
+        const auto& hexwords = src.hexwordData();
+        EXPECT_EQ(0x00080055, hexwords[0]);
+    }
+
+    {
+        EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
+            .WillOnce(Return(std::vector<bool>{false, true, false}));
+
+        SRC src{entry, ad, dataIface};
+        EXPECT_TRUE(src.valid());
+
+        const auto& hexwords = src.hexwordData();
+        EXPECT_EQ(0x00000255, hexwords[0]);
+    }
+
+    {
+        EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
+            .WillOnce(Return(std::vector<bool>{false, false, true}));
+
+        SRC src{entry, ad, dataIface};
+        EXPECT_TRUE(src.valid());
+
+        const auto& hexwords = src.hexwordData();
+        EXPECT_EQ(0x00000455, hexwords[0]);
+    }
+
+    {
+        EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
+            .WillOnce(Return(std::vector<bool>{true, true, true}));
+
+        SRC src{entry, ad, dataIface};
+        EXPECT_TRUE(src.valid());
+
+        const auto& hexwords = src.hexwordData();
+        EXPECT_EQ(0x00080655, hexwords[0]);
+    }
+}