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);