PEL: Write terminating SRC to the progress SRC

Check for incoming pels for severity type 0x51 - Critical error,
System termination. If found, fetch the SRC and add this SRC to
progress SRC interface on dbus. In addition set the terminate bit
in BMC created pels only.

Signed-off-by: Sumit Kumar <sumit_kumar@in.ibm.com>
Change-Id: I26194a26743263183dcb61e097c745c4705fa006
diff --git a/extensions/openpower-pels/data_interface.cpp b/extensions/openpower-pels/data_interface.cpp
index da499db..44370fc 100644
--- a/extensions/openpower-pels/data_interface.cpp
+++ b/extensions/openpower-pels/data_interface.cpp
@@ -38,6 +38,7 @@
 constexpr auto ledGroupManager = "xyz.openbmc_project.LED.GroupManager";
 constexpr auto logSetting = "xyz.openbmc_project.Settings";
 constexpr auto hwIsolation = "org.open_power.HardwareIsolation";
+constexpr auto bootRawProgress = "xyz.openbmc_project.State.Boot.Raw";
 } // namespace service_name
 
 namespace object_path
@@ -57,6 +58,7 @@
 constexpr auto vpdManager = "/com/ibm/VPD/Manager";
 constexpr auto logSetting = "/xyz/openbmc_project/logging/settings";
 constexpr auto hwIsolation = "/xyz/openbmc_project/hardware_isolation";
+constexpr auto bootRawSetting = "/xyz/openbmc_project/state/boot/raw0";
 } // namespace object_path
 
 namespace interface
@@ -86,6 +88,7 @@
 constexpr auto dumpEntry = "xyz.openbmc_project.Dump.Entry";
 constexpr auto dumpProgress = "xyz.openbmc_project.Common.Progress";
 constexpr auto hwIsolationCreate = "org.open_power.HardwareIsolation.Create";
+constexpr auto bootRawProgress = "xyz.openbmc_project.State.Boot.Raw";
 } // namespace interface
 
 using namespace sdbusplus::xyz::openbmc_project::State::Boot::server;
@@ -725,5 +728,19 @@
         }
     }
 }
+
+void DataInterface::createProgressSRC(
+    const uint64_t& priSRC, const std::vector<uint8_t>& srcStruct) const
+{
+    DBusValue variant = std::make_tuple(priSRC, srcStruct);
+
+    auto method = _bus.new_method_call(service_name::bootRawProgress,
+                                       object_path::bootRawSetting,
+                                       interface::dbusProperty, "Set");
+
+    method.append(interface::bootRawProgress, "Value", variant);
+
+    _bus.call(method);
+}
 } // namespace pels
 } // namespace openpower
diff --git a/extensions/openpower-pels/data_interface.hpp b/extensions/openpower-pels/data_interface.hpp
index f5053aa..b1f1e92 100644
--- a/extensions/openpower-pels/data_interface.hpp
+++ b/extensions/openpower-pels/data_interface.hpp
@@ -356,6 +356,17 @@
                                    const std::string& type,
                                    const std::string& logPath) const = 0;
 
+    /**
+     * @brief Create Progress SRC property on the boot progress
+     *        interface on a D-Bus object.
+     *
+     * @param[in] priSRC - Primary SRC value (e.g. BD8D1001)
+     * @param[in] srcStruct - Full SRC base structure
+     */
+    virtual void
+        createProgressSRC(const uint64_t& priSRC,
+                          const std::vector<uint8_t>& srcStruct) const = 0;
+
   protected:
     /**
      * @brief Sets the host on/off state and runs any
@@ -655,6 +666,17 @@
                            const std::string& type,
                            const std::string& logPath) const override;
 
+    /**
+     * @brief Create Progress SRC property on the boot progress
+     *        interface on a D-Bus object.
+     *
+     * @param[in] priSRC - Primary SRC value
+     * @param[in] srcStruct - Full SRC base structure
+     */
+    void
+        createProgressSRC(const uint64_t& priSRC,
+                          const std::vector<uint8_t>& srcStruct) const override;
+
   private:
     /**
      * @brief Reads the BMC firmware version string and puts it into
diff --git a/extensions/openpower-pels/dbus_types.hpp b/extensions/openpower-pels/dbus_types.hpp
index edb5637..ccb3f81 100644
--- a/extensions/openpower-pels/dbus_types.hpp
+++ b/extensions/openpower-pels/dbus_types.hpp
@@ -9,9 +9,11 @@
 namespace openpower::pels
 {
 
-using DBusValue = std::variant<
-    std::string, bool, std::vector<uint8_t>, std::vector<std::string>,
-    std::vector<std::tuple<std::string, std::string, std::string>>>;
+using DBusValue =
+    std::variant<std::string, bool, std::vector<uint8_t>,
+                 std::vector<std::string>,
+                 std::vector<std::tuple<std::string, std::string, std::string>>,
+                 std::tuple<uint64_t, std::vector<uint8_t>>>;
 using DBusProperty = std::string;
 using DBusInterface = std::string;
 using DBusService = std::string;
diff --git a/extensions/openpower-pels/manager.cpp b/extensions/openpower-pels/manager.cpp
index 95c53ca..331238e 100644
--- a/extensions/openpower-pels/manager.cpp
+++ b/extensions/openpower-pels/manager.cpp
@@ -163,6 +163,9 @@
         // Update System Info to Extended User Data
         pel->updateSysInfoInExtendedUserDataSection(*_dataIface);
 
+        // Check for severity 0x51 and update boot progress SRC
+        updateProgressSRC(pel);
+
         try
         {
             log<level::DEBUG>(
@@ -385,6 +388,9 @@
         log<level::INFO>(msg.c_str());
     }
 
+    // Check for severity 0x51 and update boot progress SRC
+    updateProgressSRC(pel);
+
     // Activate any resulting service indicators if necessary
     auto policy = service_indicators::getPolicy(*_dataIface);
     policy->activate(*pel);
@@ -860,5 +866,37 @@
     }
 }
 
+void Manager::updateProgressSRC(
+    std::unique_ptr<openpower::pels::PEL>& pel) const
+{
+    // Check for pel severity of type - 0x51 = critical error, system
+    // termination
+    if (pel->userHeader().severity() == 0x51)
+    {
+        auto src = pel->primarySRC();
+        if (src)
+        {
+            std::vector<uint8_t> asciiSRC = (*src)->getSrcStruct();
+            uint64_t srcRefCode = 0;
+
+            // Read bytes from offset [40-47] e.g. BD8D1001
+            for (int i = 0; i < 8; i++)
+            {
+                srcRefCode |=
+                    (static_cast<uint64_t>(asciiSRC[40 + i]) << (8 * i));
+            }
+
+            try
+            {
+                _dataIface->createProgressSRC(srcRefCode, asciiSRC);
+            }
+            catch (std::exception& e)
+            {
+                // Exception - may be no boot progress interface on dbus
+            }
+        }
+    }
+}
+
 } // namespace pels
 } // namespace openpower
diff --git a/extensions/openpower-pels/manager.hpp b/extensions/openpower-pels/manager.hpp
index a9476d5..e4c4953 100644
--- a/extensions/openpower-pels/manager.hpp
+++ b/extensions/openpower-pels/manager.hpp
@@ -253,6 +253,13 @@
      */
     uint32_t getBMCLogIdFromPELId(uint32_t pelId) override;
 
+    /**
+     * @brief Update boot progress SRC based on severity 0x51, critical error
+     *
+     * @param[in] pel - The PEL to use
+     */
+    void updateProgressSRC(std::unique_ptr<openpower::pels::PEL>& pel) const;
+
   private:
     /**
      * @brief Adds a received raw PEL to the PEL repository
diff --git a/extensions/openpower-pels/pel.cpp b/extensions/openpower-pels/pel.cpp
index ed4c3f3..a6f0af3 100644
--- a/extensions/openpower-pels/pel.cpp
+++ b/extensions/openpower-pels/pel.cpp
@@ -123,6 +123,10 @@
     auto ud = util::makeSysInfoUserDataSection(additionalData, dataIface);
     addUserDataSection(std::move(ud));
 
+    //  Check for pel severity of type - 0x51 = critical error, system
+    //  termination and update terminate bit in SRC for pels
+    updateTerminateBitInSRCSection();
+
     // Create a UserData section from AdditionalData.
     if (!additionalData.empty())
     {
@@ -571,6 +575,21 @@
     }
 }
 
+void PEL::updateTerminateBitInSRCSection()
+{
+    //  Check for pel severity of type - 0x51 = critical error, system
+    //  termination
+    if (_uh->severity() == 0x51)
+    {
+        // Get the primary SRC section
+        auto pSRC = primarySRC();
+        if (pSRC)
+        {
+            (*pSRC)->setTerminateBit();
+        }
+    }
+}
+
 namespace util
 {
 
diff --git a/extensions/openpower-pels/pel.hpp b/extensions/openpower-pels/pel.hpp
index 884379a..649357f 100644
--- a/extensions/openpower-pels/pel.hpp
+++ b/extensions/openpower-pels/pel.hpp
@@ -375,6 +375,12 @@
     nlohmann::json getCalloutJSON(const PelFFDC& ffdcFiles);
 
     /**
+     * @brief Update terminate bit in primary SRC section to this PEL object is
+     * severity set to 0x51 = critical error, system termination
+     */
+    void updateTerminateBitInSRCSection();
+
+    /**
      * @brief The PEL Private Header section
      */
     std::unique_ptr<PrivateHeader> _ph;
diff --git a/extensions/openpower-pels/src.cpp b/extensions/openpower-pels/src.cpp
index 4279a00..5984287 100644
--- a/extensions/openpower-pels/src.cpp
+++ b/extensions/openpower-pels/src.cpp
@@ -719,6 +719,11 @@
         // The PEL spec calls it a backplane, so call it that here.
         jsonInsert(ps, "Backplane CCIN", ccinString, 1);
 
+        jsonInsert(ps, "Terminate FW Error",
+                   pv::boolString.at(
+                       _hexData[3] &
+                       static_cast<uint32_t>(ErrorStatusFlags::terminateFwErr)),
+                   1);
         jsonInsert(ps, "Deconfigured",
                    pv::boolString.at(
                        _hexData[3] &
@@ -1436,5 +1441,46 @@
     }
 }
 
+std::vector<uint8_t> SRC::getSrcStruct()
+{
+    std::vector<uint8_t> data;
+    Stream stream{data};
+
+    //------ Ref section 4.3 in PEL doc---
+    //------ SRC Structure 40 bytes-------
+    // Byte-0 | Byte-1 | Byte-2 | Byte-3 |
+    // -----------------------------------
+    //   02   |   08   |   00   |   09   | ==> Header
+    //   00   |   00   |   00   |   48   | ==> Header
+    //   00   |   00   |   00   |   00   | ==> Hex data word-2
+    //   00   |   00   |   00   |   00   | ==> Hex data word-3
+    //   00   |   00   |   00   |   00   | ==> Hex data word-4
+    //   20   |   00   |   00   |   00   | ==> Hex data word-5
+    //   00   |   00   |   00   |   00   | ==> Hex data word-6
+    //   00   |   00   |   00   |   00   | ==> Hex data word-7
+    //   00   |   00   |   00   |   00   | ==> Hex data word-8
+    //   00   |   00   |   00   |   00   | ==> Hex data word-9
+    // -----------------------------------
+    //   ASCII string - 8 bytes          |
+    // -----------------------------------
+    //   ASCII space NULL - 24 bytes     |
+    // -----------------------------------
+    //_size = Base SRC struct: 8 byte header + hex data section + ASCII string
+
+    uint8_t flags = (_flags | postOPPanel);
+
+    stream << _version << flags << _reserved1B << _wordCount << _reserved2B
+           << _size;
+
+    for (auto& word : _hexData)
+    {
+        stream << word;
+    }
+
+    _asciiString->flatten(stream);
+
+    return data;
+}
+
 } // namespace pels
 } // namespace openpower
diff --git a/extensions/openpower-pels/src.hpp b/extensions/openpower-pels/src.hpp
index f0b151e..2ab5ae4 100644
--- a/extensions/openpower-pels/src.hpp
+++ b/extensions/openpower-pels/src.hpp
@@ -53,6 +53,7 @@
         additionalSections = 0x01,
         powerFaultEvent = 0x02,
         hypDumpInit = 0x04,
+        postOPPanel = 0x08,
         i5OSServiceEventBit = 0x10,
         virtualProgressSRC = 0x80
     };
@@ -63,6 +64,7 @@
      */
     enum class ErrorStatusFlags
     {
+        terminateFwErr = 0x20000000,
         deconfigured = 0x02000000,
         guarded = 0x01000000
     };
@@ -281,6 +283,22 @@
      */
     bool isBMCSRC() const;
 
+    /**
+     * @brief Set the terminate bit in hex data word 3.
+     */
+    void setTerminateBit()
+    {
+        setErrorStatusFlag(ErrorStatusFlags::terminateFwErr);
+    }
+
+    /**
+     * @brief Get the SRC structure to pass on to the boot progress dbus
+     * interface.
+     *
+     * @return std::vector<uint8_t> - SRC struct data
+     */
+    std::vector<uint8_t> getSrcStruct();
+
   private:
     /**
      * @brief Fills in the user defined hex words from the
diff --git a/test/openpower-pels/mocks.hpp b/test/openpower-pels/mocks.hpp
index 99e9783..da7c060 100644
--- a/test/openpower-pels/mocks.hpp
+++ b/test/openpower-pels/mocks.hpp
@@ -54,6 +54,9 @@
                 (const std::vector<uint8_t>&, const std::string&,
                  const std::string&),
                 (const override));
+    MOCK_METHOD(void, createProgressSRC,
+                (const uint64_t&, const std::vector<uint8_t>&),
+                (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 eced363..0643229 100644
--- a/test/openpower-pels/pel_manager_test.cpp
+++ b/test/openpower-pels/pel_manager_test.cpp
@@ -1037,3 +1037,76 @@
     EXPECT_EQ(countPELsInRepo(), 1);
     EXPECT_EQ(count, 1);
 }
+
+// Test termination bit set for pel with critical system termination severity
+TEST_F(ManagerTest, TestTerminateBitWithPELSevCriticalSysTerminate)
+{
+    const auto registry = R"(
+{
+    "PELs":
+    [
+        {
+            "Name": "xyz.openbmc_project.Error.Test",
+            "Subsystem": "power_supply",
+            "Severity": "critical_system_term",
+            "ActionFlags": ["service_action", "report"],
+            "SRC":
+            {
+                "ReasonCode": "0x2030"
+            },
+            "Documentation":
+            {
+                "Description": "A PGOOD Fault",
+                "Message": "PS had a PGOOD Fault"
+            }
+        }
+    ]
+}
+)";
+
+    auto path = getPELReadOnlyDataPath();
+    fs::create_directories(path);
+    path /= "message_registry.json";
+
+    std::ofstream registryFile{path};
+    registryFile << registry;
+    registryFile.close();
+
+    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,
+                  std::placeholders::_2, std::placeholders::_3)};
+
+    std::vector<std::string> additionalData{"FOO=BAR"};
+    std::vector<std::string> associations;
+
+    // Create the event log to create the PEL from.
+    manager.create("xyz.openbmc_project.Error.Test", 33, 0,
+                   phosphor::logging::Entry::Level::Error, additionalData,
+                   associations);
+
+    // Ensure a PEL was created in the repository
+    auto pelData = findAnyPELInRepo();
+    ASSERT_TRUE(pelData);
+
+    auto getPELData = readPELFile(*pelData);
+    PEL pel(*getPELData);
+
+    // Spot check it.  Other testcases cover the details.
+    EXPECT_TRUE(pel.valid());
+
+    // Check for terminate bit set
+    auto& hexwords = pel.primarySRC().value()->hexwordData();
+    EXPECT_EQ(hexwords[3] & 0x20000000, 0x20000000);
+}