PEL: Save the motherboard CCIN in the SRC

The CCIN field is from the CC keyword of the VINI record in the
motherboard VPD.

Save it in the first half of the second hex word of the SRC.

Also print this field when displaying the SRC section, but only for SRCs
created by this code, which it knows by checking the first 2 characters
of the ASCII string.

Signed-off-by: Matt Spinler <spinler@us.ibm.com>
Change-Id: I25f94f7fbcfd3212adf28c357e55271ea9269add
diff --git a/extensions/openpower-pels/pel.cpp b/extensions/openpower-pels/pel.cpp
index 60821b6..568b4c6 100644
--- a/extensions/openpower-pels/pel.cpp
+++ b/extensions/openpower-pels/pel.cpp
@@ -48,7 +48,7 @@
                                           timestamp);
     _uh = std::make_unique<UserHeader>(entry, severity);
 
-    auto src = std::make_unique<SRC>(entry, additionalData);
+    auto src = std::make_unique<SRC>(entry, additionalData, dataIface);
 
     auto euh = std::make_unique<ExtendedUserHeader>(dataIface, entry, *src);
 
diff --git a/extensions/openpower-pels/src.cpp b/extensions/openpower-pels/src.cpp
index db84347..4067fe8 100644
--- a/extensions/openpower-pels/src.cpp
+++ b/extensions/openpower-pels/src.cpp
@@ -29,6 +29,8 @@
 namespace rg = openpower::pels::message;
 using namespace phosphor::logging;
 
+constexpr size_t ccinSize = 4;
+
 void SRC::unflatten(Stream& stream)
 {
     stream >> _header >> _version >> _flags >> _reserved1B >> _wordCount >>
@@ -80,7 +82,8 @@
     }
 }
 
-SRC::SRC(const message::Entry& regEntry, const AdditionalData& additionalData)
+SRC::SRC(const message::Entry& regEntry, const AdditionalData& additionalData,
+         const DataInterfaceBase& dataIface)
 {
     _header.id = static_cast<uint16_t>(SectionID::primarySRC);
     _header.version = srcSectionVersion;
@@ -106,6 +109,8 @@
                   [](auto& word) { word = 0; });
     setBMCFormat();
     setBMCPosition();
+    setMotherboardCCIN(dataIface);
+
     // Partition dump status and partition boot type always 0 for BMC errors.
     //
     // TODO: Fill in other fields that aren't available yet.
@@ -160,6 +165,29 @@
     }
 }
 
+void SRC::setMotherboardCCIN(const DataInterfaceBase& dataIface)
+{
+    uint32_t ccin = 0;
+    auto ccinString = dataIface.getMotherboardCCIN();
+
+    try
+    {
+        if (ccinString.size() == ccinSize)
+        {
+            ccin = std::stoi(ccinString, 0, 16);
+        }
+    }
+    catch (std::exception& e)
+    {
+        log<level::WARNING>("Could not convert motherboard CCIN to a number",
+                            entry("CCIN=%s", ccinString.c_str()));
+        return;
+    }
+
+    // Set the first 2 bytes
+    _hexData[1] |= ccin << 16;
+}
+
 void SRC::validate()
 {
     bool failed = false;
@@ -182,16 +210,25 @@
     _valid = failed ? false : true;
 }
 
+bool SRC::isBMCSRC() const
+{
+    auto as = asciiString();
+    if (as.length() >= 2)
+    {
+        uint8_t errorType = strtoul(as.substr(0, 2).c_str(), nullptr, 16);
+        return (errorType == static_cast<uint8_t>(SRCType::bmcError) ||
+                errorType == static_cast<uint8_t>(SRCType::powerError));
+    }
+    return false;
+}
+
 std::optional<std::string> SRC::getErrorDetails(message::Registry& registry,
                                                 DetailLevel type,
                                                 bool toCache) const
 {
     const std::string jsonIndent(indentLevel, 0x20);
     std::string errorOut;
-    uint8_t errorType =
-        strtoul(asciiString().substr(0, 2).c_str(), nullptr, 16);
-    if (errorType == static_cast<uint8_t>(SRCType::bmcError) ||
-        errorType == static_cast<uint8_t>(SRCType::powerError))
+    if (isBMCSRC())
     {
         auto entry = registry.lookup("0x" + asciiString().substr(4, 4),
                                      rg::LookupType::reasonCode, toCache);
@@ -411,6 +448,20 @@
                pv::boolString.at(_flags & hypDumpInit), 1);
     jsonInsert(ps, "Power Control Net Fault",
                pv::boolString.at(isPowerFaultEvent()), 1);
+
+    if (isBMCSRC())
+    {
+        std::string ccinString;
+        uint32_t ccin = _hexData[1] >> 16;
+
+        if (ccin)
+        {
+            ccinString = getNumberString("%04X", ccin);
+        }
+        // The PEL spec calls it a backplane, so call it that here.
+        jsonInsert(ps, "Backplane CCIN", ccinString, 1);
+    }
+
     rg::Registry registry(getMessageRegistryPath() / rg::registryFileName);
     auto errorDetails = getErrorDetails(registry, DetailLevel::json);
     if (errorDetails)
diff --git a/extensions/openpower-pels/src.hpp b/extensions/openpower-pels/src.hpp
index 2296f6f..7cfa311 100644
--- a/extensions/openpower-pels/src.hpp
+++ b/extensions/openpower-pels/src.hpp
@@ -3,6 +3,7 @@
 #include "additional_data.hpp"
 #include "ascii_string.hpp"
 #include "callouts.hpp"
+#include "data_interface.hpp"
 #include "pel_types.hpp"
 #include "registry.hpp"
 #include "section.hpp"
@@ -82,8 +83,10 @@
      * @param[in] regEntry - The message registry entry for this event log
      * @param[in] additionalData - The AdditionalData properties in this event
      *                             log
+     * @param[in] dataIface - The DataInterface object
      */
-    SRC(const message::Entry& regEntry, const AdditionalData& additionalData);
+    SRC(const message::Entry& regEntry, const AdditionalData& additionalData,
+        const DataInterfaceBase& dataIface);
 
     /**
      * @brief Flatten the section into the stream
@@ -237,6 +240,13 @@
                                                DetailLevel type,
                                                bool toCache = false) const;
 
+    /**
+     * @brief Says if this SRC was created by the BMC (i.e. this code).
+     *
+     * @return bool - If created by the BMC or not
+     */
+    bool isBMCSRC() const;
+
   private:
     /**
      * @brief Fills in the user defined hex words from the
@@ -299,6 +309,13 @@
     }
 
     /**
+     * @brief Sets the motherboard CCIN hex word field
+     *
+     * @param[in] dataIface - The DataInterface object
+     */
+    void setMotherboardCCIN(const DataInterfaceBase& dataIface);
+
+    /**
      * @brief Validates the section contents
      *
      * Updates _valid (in Section) with the results.