PEL: Pass in subsystem to use with additional data

There is a requirement to pass in the subsystem to use with
the additional data field. In the process update the user
header code and SRC with the passed-in subsystem value
instead from the message registry.

Signed-off-by: Sumit Kumar <sumit_kumar@in.ibm.com>
Change-Id: I08c8c3bab100fec581df9043f4bb27b28cb556e1
diff --git a/extensions/openpower-pels/README.md b/extensions/openpower-pels/README.md
index 637a389..7f9df1d 100644
--- a/extensions/openpower-pels/README.md
+++ b/extensions/openpower-pels/README.md
@@ -120,6 +120,13 @@
 This is used to pass in an I2C bus and address to create callouts from.  See
 [here for details](#passing-callouts-in-with-the-additionaldata-property)
 
+#### PEL_SUBSYSTEM
+This keyword is used to pass in the subsystem that should be associated with
+this event log. The syntax is:
+PEL_SUBSYSTEM=<subsystem value in hex>
+e.g.
+PEL_SUBSYSTEM=0x20
+
 ### FFDC Intended For UserData PEL sections
 
 When one needs to add FFDC into the PEL UserData sections, the
diff --git a/extensions/openpower-pels/ascii_string.hpp b/extensions/openpower-pels/ascii_string.hpp
index 0970df9..0fdf124 100644
--- a/extensions/openpower-pels/ascii_string.hpp
+++ b/extensions/openpower-pels/ascii_string.hpp
@@ -72,7 +72,6 @@
      */
     std::string get() const;
 
-  private:
     /**
      * @brief Converts a byte of raw data to 2 characters
      *        and writes it to the offset.
@@ -91,6 +90,7 @@
      */
     void setByte(size_t offset, uint8_t value);
 
+  private:
     /**
      * @brief The ASCII string itself
      */
diff --git a/extensions/openpower-pels/src.cpp b/extensions/openpower-pels/src.cpp
index 5984287..f4807f5 100644
--- a/extensions/openpower-pels/src.cpp
+++ b/extensions/openpower-pels/src.cpp
@@ -363,6 +363,26 @@
 
     _asciiString = std::make_unique<src::AsciiString>(regEntry);
 
+    // Check for additional data - PEL_SUBSYSTEM
+    auto ss = additionalData.getValue("PEL_SUBSYSTEM");
+    if (ss)
+    {
+        auto eventSubsystem = std::stoul(*ss, NULL, 16);
+        std::string subsystem =
+            pv::getValue(eventSubsystem, pel_values::subsystemValues);
+        if (subsystem == "invalid")
+        {
+            log<level::WARNING>(
+                fmt::format("SRC: Invalid SubSystem value:{:#X}",
+                            eventSubsystem)
+                    .c_str());
+        }
+        else
+        {
+            _asciiString->setByte(2, eventSubsystem);
+        }
+    }
+
     addCallouts(regEntry, additionalData, jsonCallouts, dataIface);
 
     _size = baseSRCSize;
diff --git a/extensions/openpower-pels/user_header.cpp b/extensions/openpower-pels/user_header.cpp
index 34e2fa1..7aed654 100644
--- a/extensions/openpower-pels/user_header.cpp
+++ b/extensions/openpower-pels/user_header.cpp
@@ -20,6 +20,8 @@
 #include "pel_values.hpp"
 #include "severity.hpp"
 
+#include <fmt/format.h>
+
 #include <iostream>
 #include <phosphor-logging/log.hpp>
 
@@ -58,6 +60,25 @@
 
     _eventSubsystem = entry.subsystem;
 
+    // Check for additional data - PEL_SUBSYSTEM
+    auto ss = additionalData.getValue("PEL_SUBSYSTEM");
+    if (ss)
+    {
+        auto eventSubsystem = std::stoul(*ss, NULL, 16);
+        std::string subsystem =
+            pv::getValue(eventSubsystem, pel_values::subsystemValues);
+        if (subsystem == "invalid")
+        {
+            log<level::WARNING>(
+                fmt::format("UH: Invalid SubSystem value:{:#X}", eventSubsystem)
+                    .c_str());
+        }
+        else
+        {
+            _eventSubsystem = eventSubsystem;
+        }
+    }
+
     _eventScope = entry.eventScope.value_or(
         static_cast<uint8_t>(EventScope::entirePlatform));
 
diff --git a/test/openpower-pels/src_test.cpp b/test/openpower-pels/src_test.cpp
index 11b15d4..97d9b27 100644
--- a/test/openpower-pels/src_test.cpp
+++ b/test/openpower-pels/src_test.cpp
@@ -1446,3 +1446,31 @@
         EXPECT_EQ(0x00080655, hexwords[0]);
     }
 }
+
+// Test SRC with additional data - PEL_SUBSYSTEM
+TEST_F(SRCTest, TestPELSubsystem)
+{
+    message::Entry entry;
+    entry.src.type = 0xBD;
+    entry.src.reasonCode = 0xABCD;
+    entry.subsystem = 0x42;
+    entry.src.powerFault = true;
+
+    // Values for the SRC words pointed to above
+    std::vector<std::string> adData{"PEL_SUBSYSTEM=0x20"};
+    AdditionalData ad{adData};
+    NiceMock<MockDataInterface> dataIface;
+
+    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());
+
+    EXPECT_EQ(src.asciiString(), "BD20ABCD                        ");
+}
diff --git a/test/openpower-pels/user_header_test.cpp b/test/openpower-pels/user_header_test.cpp
index a4bc3cb..59199fb 100644
--- a/test/openpower-pels/user_header_test.cpp
+++ b/test/openpower-pels/user_header_test.cpp
@@ -308,3 +308,24 @@
     EXPECT_EQ(uh.severity(), 0x20);
     EXPECT_EQ(uh.actionFlags(), 0xF000);
 }
+
+// Test that the PEL Subsystem omes from the event log if any
+TEST(UserHeaderTest, UseEventLogPELSubsystem)
+{
+    using namespace openpower::pels::message;
+    Entry regEntry;
+
+    regEntry.name = "test";
+    regEntry.subsystem = 5;
+    regEntry.actionFlags = 0xC000;
+    regEntry.eventType = 1;
+    regEntry.eventScope = 2;
+
+    MockDataInterface dataIface;
+    std::vector<std::string> adData{"PEL_SUBSYSTEM=0x25"};
+    AdditionalData ad{adData};
+
+    UserHeader uh(regEntry, phosphor::logging::Entry::Level::Critical, ad,
+                  dataIface);
+    ASSERT_EQ(uh.subsystem(), 0x25);
+}