PEL support for GPIO failure

This commit logs PEL in case of GPIO line name is not valid or this line
is busy for some other purpose or I2C line itself is bad.

Test:
Tried to access some dummy GPIO line name, which doesn't exist.

root@rain111bmc:/tmp# peltool -l
{
    "0x50002D5E": {
        "SRC":                  "BD554007",
        "Message":              "GPIO line couldn't find or read.",
        "PLID":                 "0x50002D5E",
        "CreatorID":            "BMC",
        "Subsystem":            "CEC Hardware - VPD Interface",
        "Commit Time":          "03/07/2022 18:32:43",
        "Sev":                  "Predictive Error",
        "CompID":               "0x4000"
    },

root@rain111bmc:/tmp# peltool -i "0x50002D5E"
….

    "Error Details": {
        "Message":              "GPIO line couldn't be found or read."
    },
    "Valid Word Count":         "0x09",
    "Reference Code":           "BD554007",
    "Hex Word 2":               "00000055",
    "Hex Word 3":               "2E2D0010",
    "Hex Word 4":               "00000000",
    "Hex Word 5":               "00000000",
    "Hex Word 6":               "00000000",
    "Hex Word 7":               "00000000",
    "Hex Word 8":               "00000000",
    "Hex Word 9":               "00000000",
    "Callout Section": {
        "Callout Count":        "2",
        "Callouts": [{
            "FRU Type":         "Normal Hardware FRU",
            "Priority":         "Mandatory, replace all with this type as a unit",
            "Location Code":    "U78DA.ND0.WZS0042-P0-C5",
            "Part Number":      "02WF327",
            "CCIN":             "6B58",
            "Serial Number":    "Y131UF18H003"
        }, {
            "FRU Type":         "Normal Hardware FRU",
            "Priority":         "Lowest priority replacement",
            "Location Code":    "U78DA.ND0.WZS0042-P0",
            "Part Number":      "02WG676",
            "CCIN":             "2E2D",
            "Serial Number":    "Y131UF07302T"
        }]
    }
…….
……..
},
"User Data 0": {
    "Section Version": "1",
    "Sub-section type": "1",
    "Created by": "0x2000",
    "BMCState": "Ready",
    "BootState": "Unspecified",
    "ChassisState": "Off",
    "FW Version ID": "fw1020.00-41.2-275-g429803741-dirty",
    "HostState": "Off",
    "System IM": "50001001"
},
"User Data 1": {
    "Section Version": "1",
    "Sub-section type": "1",
    "Created by": "0x2000",
    "CALLOUT_IIC_ADDR": "0x0061",
    "CALLOUT_IIC_BUS": "8",
    "DESCRIPTION": "Couldn't find output line for the GPIO. Skipping this GPIO action. GPIO : SLOT10_PRSNT_EN_RSVD"
},
"User Data 2": {
    "Section Version": "1",
    "Sub-section type": "1",
    "Created by": "0x2000",
    "PEL Internal Debug Data": {
        "SRC": [
            "MRU: /sys-0/node-0/nisqually-0/ebmc-card-connector-0/ingraham-0/BMC-0",
            "I2C: bus: 8 address: 97 dest: /sys-0/node-0/nisqually-0/PCA9552-5"
        ]
    }
}
}

Signed-off-by: Alpana Kumari <alpankum@in.ibm.com>
Change-Id: I6f5582ea614e663133bac2e580ee5c0b36c3a038
diff --git a/ibm_vpd_utils.cpp b/ibm_vpd_utils.cpp
index c75c0de..fbd2968 100644
--- a/ibm_vpd_utils.cpp
+++ b/ibm_vpd_utils.cpp
@@ -6,6 +6,7 @@
 #include "defines.hpp"
 #include "vpd_exceptions.hpp"
 
+#include <boost/algorithm/string.hpp>
 #include <filesystem>
 #include <fstream>
 #include <gpiod.hpp>
@@ -614,6 +615,29 @@
     return str;
 }
 
+/*
+ * @brief Log PEL for GPIO exception
+ *
+ * @param[in] gpioErr gpioError type exception
+ * @param[in] i2cBusAddr I2C bus and address
+ */
+void logGpioPel(const string& gpioErr, const string& i2cBusAddr)
+{
+    // Get the IIC details
+    vector<string> i2cReg;
+    boost::split(i2cReg, i2cBusAddr, boost::is_any_of("-"));
+
+    PelAdditionalData additionalData{};
+    if (i2cReg.size() == 2)
+    {
+        additionalData.emplace("CALLOUT_IIC_BUS", i2cReg[0]);
+        additionalData.emplace("CALLOUT_IIC_ADDR", "0x" + i2cReg[1]);
+    }
+
+    additionalData.emplace("DESCRIPTION", gpioErr);
+    createPEL(additionalData, PelSeverity::WARNING, errIntfForGpioError);
+}
+
 void executePostFailAction(const nlohmann::json& json, const string& file)
 {
     if ((json["frus"][file].at(0)).find("postActionFail") ==
@@ -647,19 +671,32 @@
 
         if (!outputLine)
         {
-            cout << "Couldn't find output line:" << pinName
-                 << " on GPIO. Skipping...\n";
-
-            return;
+            throw runtime_error(
+                "Couldn't find output line for the GPIO. Skipping "
+                "this GPIO action.");
         }
         outputLine.request(
             {"Disable line", ::gpiod::line_request::DIRECTION_OUTPUT, 0},
             pinValue);
     }
-    catch (const system_error&)
+    catch (const exception& e)
     {
-        cerr << "Failed to set post-action GPIO" << endl;
+        string i2cBusAddr;
+        string errMsg = e.what();
+        errMsg += "\nGPIO: " + pinName;
+
+        if ((json["frus"][file].at(0)["postActionFail"].find(
+                "gpioI2CAddress")) !=
+            json["frus"][file].at(0)["postActionFail"].end())
+        {
+            i2cBusAddr =
+                json["frus"][file].at(0)["postActionFail"]["gpioI2CAddress"];
+        }
+
+        logGpioPel(errMsg, i2cBusAddr);
     }
+
+    return;
 }
 
 std::optional<bool> isPresent(const nlohmann::json& json, const string& file)
@@ -682,9 +719,9 @@
 
                 if (!presenceLine)
                 {
-                    cerr << "couldn't find presence line:" << presPinName
-                         << "\n";
-                    return false;
+                    throw runtime_error(
+                        "Couldn't find the presence line for the "
+                        "GPIO. Skipping this GPIO action.");
                 }
 
                 presenceLine.request({"Read the presence line",
@@ -694,10 +731,21 @@
 
                 return (gpioData == presPinValue);
             }
-            catch (system_error&)
+            catch (const exception& e)
             {
-                cerr << "Failed to get the presence GPIO for - " << presPinName
-                     << endl;
+                string i2cBusAddr;
+                string errMsg = e.what();
+                errMsg += " GPIO : " + presPinName;
+
+                if ((json["frus"][file].at(0)["presence"])
+                        .find("gpioI2CAddress") !=
+                    json["frus"][file].at(0)["presence"].end())
+                {
+                    i2cBusAddr =
+                        json["frus"][file].at(0)["presence"]["gpioI2CAddress"];
+                }
+
+                logGpioPel(errMsg, i2cBusAddr);
                 return false;
             }
         }
@@ -734,19 +782,29 @@
 
                 if (!outputLine)
                 {
-                    cout << "Couldn't find output line:" << pinName
-                         << " on GPIO. Skipping...\n";
-
-                    return false;
+                    throw runtime_error(
+                        "Couldn't find output line for the GPIO. "
+                        "Skipping this GPIO action.");
                 }
                 outputLine.request({"FRU pre-action",
                                     ::gpiod::line_request::DIRECTION_OUTPUT, 0},
                                    pinValue);
             }
-            catch (system_error&)
+            catch (const exception& e)
             {
-                cerr << "Failed to set pre-action for GPIO - " << pinName
-                     << endl;
+                string i2cBusAddr;
+                string errMsg = e.what();
+                errMsg += " GPIO : " + pinName;
+
+                if ((json["frus"][file].at(0)["preAction"])
+                        .find("gpioI2CAddress") !=
+                    json["frus"][file].at(0)["preAction"].end())
+                {
+                    i2cBusAddr =
+                        json["frus"][file].at(0)["preAction"]["gpioI2CAddress"];
+                }
+
+                logGpioPel(errMsg, i2cBusAddr);
                 return false;
             }
         }