Add system IM keyword to PEL userdata0 section

Read the system IM keyword via dbus and add it to the PEL userdata0 section.

Signed-off-by: Ben Tyner <ben.tyner@ibm.com>
Change-Id: I6a45e0450928e49c8789cc2f93baad03cccf90c7
diff --git a/extensions/openpower-pels/data_interface.cpp b/extensions/openpower-pels/data_interface.cpp
index 59a0d85..faf035d 100644
--- a/extensions/openpower-pels/data_interface.cpp
+++ b/extensions/openpower-pels/data_interface.cpp
@@ -68,6 +68,7 @@
 constexpr auto invMotherboard =
     "xyz.openbmc_project.Inventory.Item.Board.Motherboard";
 constexpr auto viniRecordVPD = "com.ibm.ipzvpd.VINI";
+constexpr auto vsbpRecordVPD = "com.ibm.ipzvpd.VSBP";
 constexpr auto locCode = "com.ibm.ipzvpd.Location";
 constexpr auto compatible =
     "xyz.openbmc_project.Configuration.IBMCompatibleSystem";
@@ -324,6 +325,35 @@
     return ccin;
 }
 
+std::vector<uint8_t> DataInterface::getSystemIMKeyword() const
+{
+    std::vector<uint8_t> systemIM;
+
+    try
+    {
+        auto service =
+            getService(object_path::motherBoardInv, interface::vsbpRecordVPD);
+        if (!service.empty())
+        {
+            DBusValue value;
+            getProperty(service, object_path::motherBoardInv,
+                        interface::vsbpRecordVPD, "IM", value);
+
+            systemIM = std::get<std::vector<uint8_t>>(value);
+        }
+    }
+    catch (const std::exception& e)
+    {
+        log<level::WARNING>(
+            fmt::format("Failed reading System IM property from "
+                        "Interface: {} exception: {}",
+                        interface::vsbpRecordVPD, e.what())
+                .c_str());
+    }
+
+    return systemIM;
+}
+
 void DataInterface::getHWCalloutFields(const std::string& inventoryPath,
                                        std::string& fruPartNumber,
                                        std::string& ccin,
diff --git a/extensions/openpower-pels/data_interface.hpp b/extensions/openpower-pels/data_interface.hpp
index 9894684..3bfb5dc 100644
--- a/extensions/openpower-pels/data_interface.hpp
+++ b/extensions/openpower-pels/data_interface.hpp
@@ -202,6 +202,13 @@
     virtual std::string getMotherboardCCIN() const = 0;
 
     /**
+     * @brief Returns the system IM
+     *
+     * @return std::string The system IM
+     */
+    virtual std::vector<uint8_t> getSystemIMKeyword() const = 0;
+
+    /**
      * @brief Get the fields from the inventory necessary for doing
      *        a callout on an inventory path.
      *
@@ -453,6 +460,13 @@
     std::string getMotherboardCCIN() const override;
 
     /**
+     * @brief Returns the system IM
+     *
+     * @return std::vector The system IM keyword in 4 byte vector
+     */
+    std::vector<uint8_t> getSystemIMKeyword() const override;
+
+    /**
      * @brief Get the fields from the inventory necessary for doing
      *        a callout on an inventory path.
      *
diff --git a/extensions/openpower-pels/pel.cpp b/extensions/openpower-pels/pel.cpp
index a53f8f6..1add587 100644
--- a/extensions/openpower-pels/pel.cpp
+++ b/extensions/openpower-pels/pel.cpp
@@ -28,6 +28,7 @@
 #include "stream.hpp"
 #include "user_data_formats.hpp"
 
+#include <fmt/format.h>
 #include <sys/stat.h>
 #include <unistd.h>
 
@@ -624,6 +625,19 @@
     return data;
 }
 
+void addIMKeyword(nlohmann::json& json, const DataInterfaceBase& dataIface)
+{
+    auto keyword = dataIface.getSystemIMKeyword();
+
+    std::string value{};
+
+    std::for_each(keyword.begin(), keyword.end(), [&](const auto& byte) {
+        value += fmt::format("{:02X}", byte);
+    });
+
+    json["System IM"] = value;
+}
+
 void addStatesToJSON(nlohmann::json& json, const DataInterfaceBase& dataIface)
 {
     json["BMCState"] = lastSegment('.', dataIface.getBMCState());
@@ -639,6 +653,7 @@
 
     addProcessNameToJSON(json, ad.getValue("_PID"), dataIface);
     addBMCFWVersionIDToJSON(json, dataIface);
+    addIMKeyword(json, dataIface);
     addStatesToJSON(json, dataIface);
 
     return makeJSONUserDataSection(json);
diff --git a/test/openpower-pels/mocks.hpp b/test/openpower-pels/mocks.hpp
index a6a2665..4196ee7 100644
--- a/test/openpower-pels/mocks.hpp
+++ b/test/openpower-pels/mocks.hpp
@@ -43,6 +43,7 @@
                 (const override));
     MOCK_METHOD(void, setFunctional, (const std::string&, bool),
                 (const override));
+    MOCK_METHOD(std::vector<uint8_t>, getSystemIMKeyword, (), (const override));
 
     void changeHostState(bool newState)
     {
diff --git a/test/openpower-pels/pel_test.cpp b/test/openpower-pels/pel_test.cpp
index 1842a80..3fa3d55 100644
--- a/test/openpower-pels/pel_test.cpp
+++ b/test/openpower-pels/pel_test.cpp
@@ -402,6 +402,8 @@
     EXPECT_CALL(dataIface, getBMCState()).WillOnce(Return("State.Ready"));
     EXPECT_CALL(dataIface, getChassisState()).WillOnce(Return("State.On"));
     EXPECT_CALL(dataIface, getHostState()).WillOnce(Return("State.Off"));
+    EXPECT_CALL(dataIface, getSystemIMKeyword())
+        .WillOnce(Return(std::vector<uint8_t>{0, 1, 0x55, 0xAA}));
 
     std::string pid = "_PID=" + std::to_string(getpid());
     std::vector<std::string> ad{pid};
@@ -439,6 +441,9 @@
 
     state = json["HostState"].get<std::string>();
     EXPECT_EQ(state, "Off");
+
+    auto keyword = json["System IM"].get<std::string>();
+    EXPECT_EQ(keyword, "000155AA");
 }
 
 // Test that the sections that override