PASS 2 Planar support for vpd parser

The system has two planar passes - Pass1 & Pass2 Planars.
Pass1 Planar a buffer connects all pcie cards, Whereas
Pass2 Planar has mux connection established for pcie card selection.

ibm-read-vpd picks up the right deviceTree and json
(either 2UPass1/2UPass2/4UPass1/4UPass2)
based on the HW and IM kw of the planar.

Test-
tested on Rainier 2U simics

root@rainier:/tmp# ./ibm-read-vpd  -f /tmp/systemVpd
imKeyword- 50001001, HW - 0001
Processing with this JSON - /usr/share/vpd/50001001_v1.json

root@rainier:/tmp# ./ibm-read-vpd  -f /tmp/systemVpd
imKeyword- 50001001, HW - 0011
Processing with this JSON - /usr/share/vpd/50001001_v2.json

root@rainier:/tmp# ./ibm-read-vpd  -f /tmp/systemVpd
imKeyword- 50001000, HW - 0011
Processing with this JSON - /usr/share/vpd/50001000_v2.json

root@rainier:/tmp# ./ibm-read-vpd  -f /tmp/systemVpd
imKeyword- 50001000, HW - 0001
Processing with this JSON - /usr/share/vpd/50001000_v1.json
root@rainier:/tmp#

Signed-off-by: Alpana Kumari <alpankum@in.ibm.com>
Change-Id: I36de12d9697039e61e6ad569e1e119f001e782f6
diff --git a/const.hpp b/const.hpp
index 6ab10e5..92107fa 100644
--- a/const.hpp
+++ b/const.hpp
@@ -49,8 +49,10 @@
 constexpr uint8_t EXP_LOCATIN_CODE_MIN_LENGTH = 17;
 static constexpr auto SE_KWD_LENGTH = 7;
 static constexpr auto INVALID_NODE_NUMBER = -1;
-static constexpr auto RAINIER_2U = "50001001";
-static constexpr auto RAINIER_4U = "50001000";
+static constexpr auto RAINIER_2U = "50001001_0001";
+static constexpr auto RAINIER_2U_V2 = "50001001_0002";
+static constexpr auto RAINIER_4U = "50001000_0001";
+static constexpr auto RAINIER_4U_V2 = "50001000_0002";
 static constexpr auto RAINIER_1S4U = "50001002";
 static constexpr auto EVEREST = "50003000";
 
diff --git a/ibm_vpd_app.cpp b/ibm_vpd_app.cpp
index 089ad9e..6532982 100644
--- a/ibm_vpd_app.cpp
+++ b/ibm_vpd_app.cpp
@@ -40,7 +40,9 @@
 
 static const deviceTreeMap deviceTreeSystemTypeMap = {
     {RAINIER_2U, "conf-aspeed-bmc-ibm-rainier.dtb"},
+    {RAINIER_2U_V2, "conf-aspeed-bmc-ibm-rainier-v2.dtb"},
     {RAINIER_4U, "conf-aspeed-bmc-ibm-rainier-4u.dtb"},
+    {RAINIER_4U_V2, "conf-aspeed-bmc-ibm-rainier-4u-v2.dtb"},
     {RAINIER_1S4U, "conf-aspeed-bmc-ibm-rainier-1s4u.dtb"},
     {EVEREST, "conf-aspeed-bmc-ibm-everest.dtb"}};
 
@@ -807,59 +809,16 @@
 
     if (isSystemVpd)
     {
-        vector<uint8_t> imVal;
+        string systemJson{};
         if constexpr (is_same<T, Parsed>::value)
         {
-            auto property = vpdMap.find("VSBP");
-            if (property != vpdMap.end())
-            {
-                auto value = (property->second).find("IM");
-                if (value != (property->second).end())
-                {
-                    copy(value->second.begin(), value->second.end(),
-                         back_inserter(imVal));
-                }
-            }
+            // pick the right system json
+            systemJson = getSystemsJson(vpdMap);
         }
 
-        fs::path target;
+        fs::path target = systemJson;
         fs::path link = INVENTORY_JSON_SYM_LINK;
 
-        ostringstream oss;
-        for (auto& i : imVal)
-        {
-            oss << setw(2) << setfill('0') << hex << static_cast<int>(i);
-        }
-        string imValStr = oss.str();
-
-        if ((imValStr == RAINIER_4U) || // 4U
-            (imValStr == RAINIER_1S4U))
-        {
-            target = INVENTORY_JSON_4U;
-        }
-        else if (imValStr == RAINIER_2U) // 2U
-        {
-            target = INVENTORY_JSON_2U;
-        }
-        else if (imValStr == EVEREST)
-        {
-            target = INVENTORY_JSON_EVEREST;
-        }
-        else
-        {
-            PelAdditionalData additionalData{};
-            const string& baseFruInventoryPath =
-                js["frus"][filePath][0]["inventoryPath"].get_ref<string&>();
-            additionalData.emplace("CALLOUT_INVENTORY_PATH",
-                                   INVENTORY_PATH + baseFruInventoryPath);
-            additionalData.emplace(
-                "DESCRIPTION", "System IM value is erroneous/not supported.");
-            additionalData.emplace("INVALID IM VALUE", imValStr);
-            createPEL(additionalData, PelSeverity::ERROR, errIntfForInvalidVPD);
-            throw runtime_error(
-                "Erroneous/Unsupported IM in System VPD. PEL logged.");
-        }
-
         // Create the directory for hosting the symlink
         fs::create_directories(VPD_FILES_PATH);
         // unlink the symlink previously created (if any)
@@ -875,15 +834,50 @@
         inventory::ObjectMap primeObject = primeInventory(js, vpdMap);
         objects.insert(primeObject.begin(), primeObject.end());
 
-        // set the U-boot environment variable for device-tree
-        setDevTreeEnv(imValStr);
-
         // if system VPD has been restored at standby, update the EEPROM
         for (const auto& item : updatedEeproms)
         {
             updateHardware(get<0>(item), get<1>(item), get<2>(item),
                            get<3>(item));
         }
+
+        // set the U-boot environment variable for device-tree
+        if constexpr (is_same<T, Parsed>::value)
+        {
+            const string imKeyword = getIM(vpdMap);
+            const string hwKeyword = getHW(vpdMap);
+            string systemType = imKeyword;
+
+            // check If system has constraint then append HW version to it.
+            ifstream sysJson(SYSTEM_JSON);
+            if (!sysJson)
+            {
+                throw((VpdJsonException("Failed to access Json path",
+                                        SYSTEM_JSON)));
+            }
+
+            try
+            {
+                auto systemJson = json::parse(sysJson);
+            }
+            catch (json::parse_error& ex)
+            {
+                throw((VpdJsonException("System Json parsing failed",
+                                        SYSTEM_JSON)));
+            }
+
+            if (systemJson["system"].find(imKeyword) !=
+                systemJson["system"].end())
+            {
+                if (systemJson["system"][imKeyword].find("constraint") !=
+                    systemJson["system"][imKeyword].end())
+                {
+                    systemType += "_" + hwKeyword;
+                }
+            }
+
+            setDevTreeEnv(systemType);
+        }
     }
 
     // Notify PIM
diff --git a/ibm_vpd_utils.cpp b/ibm_vpd_utils.cpp
index 6577969..15e3116 100644
--- a/ibm_vpd_utils.cpp
+++ b/ibm_vpd_utils.cpp
@@ -318,5 +318,107 @@
     return vpdType::INVALID_VPD_FORMAT;
 }
 
+const string getIM(const Parsed& vpdMap)
+{
+    Binary imVal;
+    auto property = vpdMap.find("VSBP");
+    if (property != vpdMap.end())
+    {
+        auto kw = (property->second).find("IM");
+        if (kw != (property->second).end())
+        {
+            copy(kw->second.begin(), kw->second.end(), back_inserter(imVal));
+        }
+    }
+
+    ostringstream oss;
+    for (auto& i : imVal)
+    {
+        oss << setw(2) << setfill('0') << hex << static_cast<int>(i);
+    }
+
+    return oss.str();
+}
+
+const string getHW(const Parsed& vpdMap)
+{
+    Binary hwVal;
+    auto prop = vpdMap.find("VINI");
+    if (prop != vpdMap.end())
+    {
+        auto kw = (prop->second).find("HW");
+        if (kw != (prop->second).end())
+        {
+            copy(kw->second.begin(), kw->second.end(), back_inserter(hwVal));
+        }
+    }
+
+    ostringstream hwString;
+    for (auto& i : hwVal)
+    {
+        hwString << setw(2) << setfill('0') << hex << static_cast<int>(i);
+    }
+
+    return hwString.str();
+}
+
+string getSystemsJson(const Parsed& vpdMap)
+{
+    string jsonPath = "/usr/share/vpd/";
+    string jsonName{};
+
+    ifstream systemJson(SYSTEM_JSON);
+    if (!systemJson)
+    {
+        throw((VpdJsonException("Failed to access Json path", SYSTEM_JSON)));
+    }
+
+    try
+    {
+        auto js = json::parse(systemJson);
+
+        const string hwKeyword = getHW(vpdMap);
+        const string imKeyword = getIM(vpdMap);
+
+        if (js.find("system") == js.end())
+        {
+            throw runtime_error("Invalid systems Json");
+        }
+
+        if (js["system"].find(imKeyword) == js["system"].end())
+        {
+            throw runtime_error(
+                "Invalid system. This system type is not present "
+                "in the systemsJson. IM: " +
+                imKeyword);
+        }
+
+        if ((js["system"][imKeyword].find("constraint") !=
+             js["system"][imKeyword].end()) &&
+            (hwKeyword == js["system"][imKeyword]["constraint"]["HW"]))
+        {
+            jsonName = js["system"][imKeyword]["constraint"]["json"];
+        }
+        else if (js["system"][imKeyword].find("default") !=
+                 js["system"][imKeyword].end())
+        {
+            jsonName = js["system"][imKeyword]["default"];
+        }
+        else
+        {
+            throw runtime_error(
+                "Bad System json. Neither constraint nor default found");
+        }
+
+        jsonPath += jsonName;
+    }
+
+    catch (json::parse_error& ex)
+    {
+        throw(VpdJsonException("Json Parsing failed", SYSTEM_JSON));
+    }
+    return jsonPath;
+}
+
 } // namespace vpd
-} // namespace openpower
+} // namespace openpower
\ No newline at end of file
diff --git a/ibm_vpd_utils.hpp b/ibm_vpd_utils.hpp
index 9aaebee..29996a9 100644
--- a/ibm_vpd_utils.hpp
+++ b/ibm_vpd_utils.hpp
@@ -1,6 +1,7 @@
 #pragma once
 
 #include "const.hpp"
+#include "store.hpp"
 #include "types.hpp"
 
 #include <iostream>
@@ -162,5 +163,24 @@
     return stdOutput;
 }
 
+/** @brief This API checks for IM and HW keywords, and based
+ *         on these values decides which system json to be used.
+ *  @param[in] vpdMap -  parsed vpd
+ *  @returns System json path
+ */
+string getSystemsJson(const Parsed& vpdMap);
+
+/** @brief Reads HW Keyword from the vpd
+ *  @param[in] vpdMap -  parsed vpd
+ *  @returns value of HW Keyword
+ */
+const string getHW(const Parsed& vpdMap);
+
+/** @brief Reads IM Keyword from the vpd
+ *  @param[in] vpdMap -  parsed vpd
+ *  @returns value of IM Keyword
+ */
+const string getIM(const Parsed& vpdMap);
+
 } // namespace vpd
-} // namespace openpower
+} // namespace openpower
\ No newline at end of file
diff --git a/meson.build b/meson.build
index e05edc3..f9c340a 100644
--- a/meson.build
+++ b/meson.build
@@ -48,7 +48,8 @@
                        'INVENTORY_JSON_2U': '"'+get_option('INVENTORY_JSON_2U')+'"',
                        'INVENTORY_JSON_4U': '"'+get_option('INVENTORY_JSON_4U')+'"',
                        'INVENTORY_JSON_EVEREST': '"'+get_option('INVENTORY_JSON_EVEREST')+'"',
-                       'DBUS_PROP_JSON': '"'+get_option('DBUS_PROP_JSON')+'"'
+                       'DBUS_PROP_JSON': '"'+get_option('DBUS_PROP_JSON')+'"',
+                       'SYSTEM_JSON' : '"'+get_option('SYSTEM_JSON')+'"'
                        }
   )
 
diff --git a/meson_options.txt b/meson_options.txt
index f5c839c..20697d6 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -21,3 +21,4 @@
 option('INVENTORY_JSON_4U',type: 'string', value: '/usr/share/vpd/50001000.json',  description: 'Inventory JSON for 4U system.')
 option('INVENTORY_JSON_EVEREST',type: 'string', value: '/usr/share/vpd/50003000.json',  description: 'Inventory JSON for Everest system.')
 option('DBUS_PROP_JSON',type: 'string', value: '/usr/share/vpd/dbus_properties.json',  description: 'Json which contains properties specific to dbus.')
+option('SYSTEM_JSON',type: 'string', value: '/usr/share/vpd/systems.json',  description: 'JSON file used to pick the right system json')