PEL: Add boot progress code to SRC hex data

Add the first 8 characters from the ASCII string field of the current
progress SRC, taken from the xyz.openbmc_project.State.Boot.Raw D-Bus
interface, to SRC hex word 4 when creating a PEL.

This is how the field is defined in the PEL spec, and is to help with
debug so that one can know which part of the boot was occurring when
the PEL was created.  Note that at this point most progress codes are
sent down from one of the host firmware subsystems and not created by
the BMC.

The field is only inserted into the SRC if those characters are present
and represent a valid 4 byte number, such as "C7004000".  This is then
represented as 0xC7004000 in the SRC word. Otherwise, the word is left
at a value of zero.

For example:
...
    "Valid Word Count":         "0x09",
    "Reference Code":           "BD8D1001",
    "Hex Word 2":               "00080455",
    "Hex Word 3":               "2E2D0010",
    "Hex Word 4":               "C7004000", <---Progress code
    "Hex Word 5":               "00000000",
    "Hex Word 6":               "00000005",
    "Hex Word 7":               "00000000",
    "Hex Word 8":               "00000000",
    "Hex Word 9":               "00000000"
...

Signed-off-by: Vijay Lobo <vijaylobo@gmail.com>
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
Change-Id: Iba41e88626c0e081e5759b994e3630ef8b12daf4
diff --git a/extensions/openpower-pels/data_interface.cpp b/extensions/openpower-pels/data_interface.cpp
index e6b95a3..0e31b0e 100644
--- a/extensions/openpower-pels/data_interface.cpp
+++ b/extensions/openpower-pels/data_interface.cpp
@@ -44,8 +44,8 @@
 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";
 constexpr auto biosConfigMgr = "xyz.openbmc_project.BIOSConfigManager";
+constexpr auto bootRawProgress = "xyz.openbmc_project.State.Boot.Raw";
 } // namespace service_name
 
 namespace object_path
@@ -65,8 +65,8 @@
 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";
 constexpr auto biosConfigMgr = "/xyz/openbmc_project/bios_config/manager";
+constexpr auto bootRawProgress = "/xyz/openbmc_project/state/boot/raw0";
 } // namespace object_path
 
 namespace interface
@@ -96,10 +96,10 @@
 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";
 constexpr auto hwIsolationEntry = "xyz.openbmc_project.HardwareIsolation.Entry";
 constexpr auto association = "xyz.openbmc_project.Association";
 constexpr auto biosConfigMgr = "xyz.openbmc_project.BIOSConfig.Manager";
+constexpr auto bootRawProgress = "xyz.openbmc_project.State.Boot.Raw";
 } // namespace interface
 
 using namespace sdbusplus::xyz::openbmc_project::State::Boot::server;
@@ -769,7 +769,7 @@
     DBusValue variant = std::make_tuple(priSRC, srcStruct);
 
     auto method = _bus.new_method_call(service_name::bootRawProgress,
-                                       object_path::bootRawSetting,
+                                       object_path::bootRawProgress,
                                        interface::dbusProperty, "Set");
 
     method.append(interface::bootRawProgress, "Value", variant);
@@ -868,5 +868,18 @@
     }
     return ids;
 }
+
+std::vector<uint8_t> DataInterface::getRawProgressSRC(void) const
+{
+    using RawProgressProperty = std::tuple<uint64_t, std::vector<uint8_t>>;
+
+    DBusValue value;
+    getProperty(service_name::bootRawProgress, object_path::bootRawProgress,
+                interface::bootRawProgress, "Value", value);
+
+    const auto& rawProgress = std::get<RawProgressProperty>(value);
+    return std::get<1>(rawProgress);
+}
+
 } // namespace pels
 } // namespace openpower
diff --git a/extensions/openpower-pels/data_interface.hpp b/extensions/openpower-pels/data_interface.hpp
index e66cd0c..dbc4874 100644
--- a/extensions/openpower-pels/data_interface.hpp
+++ b/extensions/openpower-pels/data_interface.hpp
@@ -462,6 +462,14 @@
      */
     virtual std::vector<uint32_t> getLogIDWithHwIsolation() const = 0;
 
+    /**
+     * @brief Returns the latest raw progress SRC from the State.Boot.Raw
+     *        D-Bus interface.
+     *
+     * @return std::vector<uint8_t> - The progress SRC bytes
+     */
+    virtual std::vector<uint8_t> getRawProgressSRC() const = 0;
+
   protected:
     /**
      * @brief Sets the host on/off state and runs any
@@ -780,6 +788,14 @@
      */
     std::vector<uint32_t> getLogIDWithHwIsolation() const override;
 
+    /**
+     * @brief Returns the latest raw progress SRC from the State.Boot.Raw
+     *        D-Bus interface.
+     *
+     * @return std::vector<uint8_t>: The progress SRC bytes
+     */
+    std::vector<uint8_t> getRawProgressSRC() 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 33e6082..75b291a 100644
--- a/extensions/openpower-pels/src.cpp
+++ b/extensions/openpower-pels/src.cpp
@@ -347,6 +347,7 @@
     //   P: Platform dump status
     //  FF: SRC format, set below
 
+    setProgressCode(dataIface);
     setDumpStatus(dataIface);
     setBMCFormat();
     setBMCPosition();
@@ -1507,5 +1508,56 @@
     return data;
 }
 
+void SRC::setProgressCode(const DataInterfaceBase& dataIface)
+{
+    std::vector<uint8_t> progressSRC;
+
+    try
+    {
+        progressSRC = dataIface.getRawProgressSRC();
+    }
+    catch (const std::exception& e)
+    {
+        log<level::ERR>(
+            fmt::format("Error getting progress code: {}", e.what()).c_str());
+        return;
+    }
+
+    _hexData[2] = getProgressCode(progressSRC);
+}
+
+uint32_t SRC::getProgressCode(std::vector<uint8_t>& rawProgressSRC)
+{
+    uint32_t progressCode = 0;
+
+    // A valid progress SRC is at least 72 bytes
+    if (rawProgressSRC.size() < 72)
+    {
+        return progressCode;
+    }
+
+    try
+    {
+        // The ASCII string field in progress SRCs starts at offset 40.
+        // Take the first 8 characters to put in the uint32:
+        //   "CC009189" -> 0xCC009189
+        Stream stream{rawProgressSRC, 40};
+        src::AsciiString aString{stream};
+        auto progressCodeString = aString.get().substr(0, 8);
+
+        if (std::all_of(progressCodeString.begin(), progressCodeString.end(),
+                        [](char c) {
+                            return std::isxdigit(static_cast<unsigned char>(c));
+                        }))
+        {
+            progressCode = std::stoul(progressCodeString, nullptr, 16);
+        }
+    }
+    catch (const std::exception& e)
+    {}
+
+    return progressCode;
+}
+
 } // namespace pels
 } // namespace openpower
diff --git a/extensions/openpower-pels/src.hpp b/extensions/openpower-pels/src.hpp
index 62b7699..3e6e519 100644
--- a/extensions/openpower-pels/src.hpp
+++ b/extensions/openpower-pels/src.hpp
@@ -298,6 +298,16 @@
      */
     std::vector<uint8_t> getSrcStruct();
 
+    /**
+     * @brief Extracts the first 8 characters of the ASCII String field
+     *        from the raw progress SRC and converts it to a uint32_t.
+     *
+     * @param[in] rawProgressSRC - The progress SRC bytes
+     *
+     * @return uint32_t - The code, like 0xCC0099EE from "CC0099EE"
+     */
+    static uint32_t getProgressCode(std::vector<uint8_t>& rawProgressSRC);
+
   private:
     /**
      * @brief Fills in the user defined hex words from the
@@ -367,6 +377,13 @@
     void setMotherboardCCIN(const DataInterfaceBase& dataIface);
 
     /**
+     * @brief Sets the progress code hex word field
+     *
+     * @param[in] dataIface - The DataInterface object
+     */
+    void setProgressCode(const DataInterfaceBase& dataIface);
+
+    /**
      * @brief Sets an error status bit in the SRC.
      *
      * @param[in] flag - The flag to set
diff --git a/test/openpower-pels/mocks.hpp b/test/openpower-pels/mocks.hpp
index ef00397..de7f757 100644
--- a/test/openpower-pels/mocks.hpp
+++ b/test/openpower-pels/mocks.hpp
@@ -58,6 +58,7 @@
                 (const override));
     MOCK_METHOD(std::vector<uint32_t>, getLogIDWithHwIsolation, (),
                 (const override));
+    MOCK_METHOD(std::vector<uint8_t>, getRawProgressSRC, (), (const override));
 
     void changeHostState(bool newState)
     {
diff --git a/test/openpower-pels/src_test.cpp b/test/openpower-pels/src_test.cpp
index a4c5628..f072508 100644
--- a/test/openpower-pels/src_test.cpp
+++ b/test/openpower-pels/src_test.cpp
@@ -1461,3 +1461,101 @@
 
     EXPECT_EQ(src.asciiString(), "BD20ABCD                        ");
 }
+
+void setAsciiString(std::vector<uint8_t>& src, const std::string& value)
+{
+    assert(40 + value.size() <= src.size());
+
+    for (size_t i = 0; i < value.size(); i++)
+    {
+        src[40 + i] = value[i];
+    }
+}
+
+TEST_F(SRCTest, TestGetProgressCode)
+{
+    {
+        // A real SRC with CC009184
+        std::vector<uint8_t> src{
+            2,  8,   0,  9,   0,   0,  0,  72, 0,  0,  0,  224, 0,  0,  0,
+            0,  204, 0,  145, 132, 0,  0,  0,  0,  0,  0,  0,   0,  0,  0,
+            0,  0,   0,  0,   0,   0,  0,  0,  0,  0,  67, 67,  48, 48, 57,
+            49, 56,  52, 32,  32,  32, 32, 32, 32, 32, 32, 32,  32, 32, 32,
+            32, 32,  32, 32,  32,  32, 32, 32, 32, 32, 32, 32};
+
+        EXPECT_EQ(SRC::getProgressCode(src), 0xCC009184);
+    }
+
+    {
+        // A real SRC with STANDBY
+        std::vector<uint8_t> src{
+            2,  0,  0,  1,  0,  0,  0,  72, 0,  0,  0,  0,  0,  0,  0,
+            0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+            0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  83, 84, 65, 78, 68,
+            66, 89, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+            32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32};
+
+        EXPECT_EQ(SRC::getProgressCode(src), 0);
+    }
+
+    {
+        // A real SRC with CC009184, but 1 byte too short
+        std::vector<uint8_t> src{
+            2,  8,   0,  9,   0,   0,  0,  72, 0,  0,  0,  224, 0,  0,  0,
+            0,  204, 0,  145, 132, 0,  0,  0,  0,  0,  0,  0,   0,  0,  0,
+            0,  0,   0,  0,   0,   0,  0,  0,  0,  0,  67, 67,  48, 48, 57,
+            49, 56,  52, 32,  32,  32, 32, 32, 32, 32, 32, 32,  32, 32, 32,
+            32, 32,  32, 32,  32,  32, 32, 32, 32, 32, 32, 32};
+        src.resize(71);
+        EXPECT_EQ(SRC::getProgressCode(src), 0);
+    }
+
+    {
+        // A few different ones
+        const std::map<std::string, uint32_t> progressCodes{
+            {"12345678", 0x12345678}, {"ABCDEF00", 0xABCDEF00},
+            {"abcdef00", 0xABCDEF00}, {"X1234567", 0},
+            {"1234567X", 0},          {"1       ", 0}};
+
+        std::vector<uint8_t> src(72, 0x0);
+
+        for (const auto& [code, expected] : progressCodes)
+        {
+            setAsciiString(src, code);
+            EXPECT_EQ(SRC::getProgressCode(src), expected);
+        }
+
+        // empty
+        src.clear();
+        EXPECT_EQ(SRC::getProgressCode(src), 0);
+    }
+}
+
+// Test progress is in right SRC hex data field
+TEST_F(SRCTest, TestProgressCodeField)
+{
+    message::Entry entry;
+    entry.src.type = 0xBD;
+    entry.src.reasonCode = 0xABCD;
+    entry.subsystem = 0x42;
+
+    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}));
+    EXPECT_CALL(dataIface, getRawProgressSRC())
+        .WillOnce(Return(std::vector<uint8_t>{
+            2,  8,   0,  9,   0,   0,  0,  72, 0,  0,  0,  224, 0,  0,  0,
+            0,  204, 0,  145, 132, 0,  0,  0,  0,  0,  0,  0,   0,  0,  0,
+            0,  0,   0,  0,   0,   0,  0,  0,  0,  0,  67, 67,  48, 48, 57,
+            49, 56,  52, 32,  32,  32, 32, 32, 32, 32, 32, 32,  32, 32, 32,
+            32, 32,  32, 32,  32,  32, 32, 32, 32, 32, 32, 32}));
+
+    SRC src{entry, ad, dataIface};
+    EXPECT_TRUE(src.valid());
+
+    // Verify that the hex vlue is set at the right hexword
+    EXPECT_EQ(src.hexwordData()[2], 0xCC009184);
+}