PEL creation in case of HW/SW failure.

Creation of PEL in case the parser process fails to parse and/or publish
VPD data.
This commit handles both hardware or software failure and creates PEL
accordingly.

Tested on Simics.

Test procedure:
Step1 : Copy ibm_read_vpd in /tmp folder on simics
Step2 : Run ibm_read_vpd exe with a vpd file path having invalid VPD/ECC or
        invalid JSON path. In this case we have given path to VPD file with
        invalid VPD data.
        command-> ./ibm_read_vpd --file <vpd_file_path>
Step3 : After the execution is over, look for PEL logged using command
        "peltool -a"

PEL logged incase of invalid VPD:
[
{
"Private Header": {
    "Section Version":          "1",
    "Sub-section type":         "0",
    "Created by":               "0x4000",
    "Created at":               "11/27/2020 04:40:00",
    "Committed at":             "11/27/2020 04:40:00",
    "Creator Subsystem":        "BMC",
    "CSSVER":                   "",
    "Platform Log Id":          "0x50000002",
    "Entry Id":                 "0x50000002",
    "BMC Event Log Id":         "2"
},
"User Header": {
    "Section Version":          "1",
    "Sub-section type":         "0",
    "Log Committed by":         "0x2000",
    "Subsystem":                "CEC Hardware: VPD Interface",
    "Event Scope":              "Entire Platform",
    "Event Severity":           "Unrecoverable Error",
    "Event Type":               "Not Applicable",
    "Action Flags": [
                                "Service Action Required",
                                "Report Externally",
                                "HMC Call Home"
    ],
    "Host Transmission":        "Not Sent"
},
"Primary SRC": {
    "Section Version":          "1",
    "Sub-section type":         "1",
    "Created by":               "0x4000",
    "SRC Version":              "0x02",
    "SRC Format":               "0x55",
    "Virtual Progress SRC":     "False",
    "I5/OS Service Event Bit":  "False",
    "Hypervisor Dump Initiated":"False",
    "Power Control Net Fault":  "False",
    "Backplane CCIN":           "2E2D",
    "Error Details": {
        "Message":              "A VPD data exception occurred."
    },
    "Valid Word Count":         "0x09",
    "Reference Code":           "BD554001",
    "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":        "1",
        "Callouts": [{
            "FRU Type":         "Normal Hardware FRU",
            "Priority":         "Mandatory, replace all with this type as a unit",
            "Location Code":    "U78DA.ND1.1234567-P0",
            "Part Number":      "F191014",
            "CCIN":             "2E2D",
            "Serial Number":    "YL2E2D010000"
        }]
    }
},
"Extended User Header": {
    "Section Version":          "1",
    "Sub-section type":         "0",
    "Created by":               "0x2000",
    "Reporting Machine Type":   "9105-22A",
    "Reporting Serial Number":  "SIMP10R",
    "FW Released Ver":          "",
    "FW SubSys Version":        "fw1020.00-6",
    "Common Ref Time":          "00/00/0000 00:00:00",
    "Symptom Id Len":           "20",
    "Symptom Id":               "BD554001_2E2D0010"
},
"Failing MTMS": {
    "Section Version":          "1",
    "Sub-section type":         "0",
    "Created by":               "0x2000",
    "Machine Type Model":       "9105-22A",
    "Serial Number":            "SIMP10R"
},
"User Data 0": {
    "Section Version": "1",
    "Sub-section type": "1",
    "Created by": "0x2000",
    "BMC Version ID": "fw1020.00-6-22-gbbd23f832",
    "BMCState": "Ready",
    "ChassisState": "Off",
    "HostState": "Off",
    "Process Name": "Unknown"
},
"User Data 1": {
    "Section Version": "1",
    "Sub-section type": "1",
    "Created by": "0x2000",
    "CALLOUT_INVENTORY_PATH": "/xyz/openbmc_project/inventory/system/chassis/motherboard",
    "DESCRIPTION": "Invalid VPD data"
}
}
]

Signed-off-by: Sunny Srivastava <sunnsr25@in.ibm.com>
Change-Id: Ieb434bb45b4051d8b7b6d4c9022984d5471fc855
diff --git a/const.hpp b/const.hpp
index 40ffc62..e72abff 100644
--- a/const.hpp
+++ b/const.hpp
@@ -69,6 +69,18 @@
 constexpr auto kwdVpdInf = "com.ibm.ipzvpd.VINI";
 constexpr auto memVpdInf = "com.ibm.ipzvpd.VINI";
 constexpr auto ipzVpdInf = "com.ibm.ipzvpd.";
+constexpr auto offsetJsonDirectory = "/var/lib/vpd/";
+constexpr auto mapperObjectPath = "/xyz/openbmc_project/object_mapper";
+constexpr auto mapperInterface = "xyz.openbmc_project.ObjectMapper";
+constexpr auto mapperDestination = "xyz.openbmc_project.ObjectMapper";
+constexpr auto loggerObjectPath = "/xyz/openbmc_project/logging";
+constexpr auto loggerCreateInterface = "xyz.openbmc_project.Logging.Create";
+constexpr auto errIntfForBlankSystemVPD = "com.ibm.VPD.Error.BlankSystemVPD";
+constexpr auto errIntfForInvalidVPD = "com.ibm.VPD.Error.InvalidVPD";
+constexpr auto errIntfForStreamFail = "com.ibm.VPD.Error.InavlidEepromPath";
+constexpr auto errIntfForEccCheckFail = "com.ibm.VPD.Error.EccCheckFailed";
+constexpr auto errIntfForJsonFailure = "com.ibm.VPD.Error.InvalidJson";
+constexpr auto errIntfForBusFailure = "com.ibm.VPD.Error.DbusFailure";
 
 namespace lengths
 {
diff --git a/ibm_vpd_app.cpp b/ibm_vpd_app.cpp
index e4c2c06..04bcbba 100644
--- a/ibm_vpd_app.cpp
+++ b/ibm_vpd_app.cpp
@@ -6,6 +6,7 @@
 #include "memory_vpd_parser.hpp"
 #include "parser_factory.hpp"
 #include "utils.hpp"
+#include "vpd_exceptions.hpp"
 
 #include <ctype.h>
 
@@ -30,6 +31,7 @@
 using namespace openpower::vpd::inventory;
 using namespace openpower::vpd::memory::parser;
 using namespace openpower::vpd::parser::interface;
+using namespace openpower::vpd::exceptions;
 
 static const deviceTreeMap deviceTreeSystemTypeMap = {
     {RAINIER_2U, "conf@aspeed-bmc-ibm-rainier-2u.dtb"},
@@ -565,6 +567,15 @@
 int main(int argc, char** argv)
 {
     int rc = 0;
+    string file{};
+    json js{};
+
+    // map to hold additional data in case of logging pel
+    PelAdditionalData additionalData{};
+
+    // this is needed to hold base fru inventory path in case there is ECC or
+    // vpd exception while parsing the file
+    std::string baseFruInventoryPath = {};
 
     try
     {
@@ -589,7 +600,20 @@
 
         // Make sure that the file path we get is for a supported EEPROM
         ifstream inventoryJson(jsonToParse);
-        auto js = json::parse(inventoryJson);
+        if (!inventoryJson)
+        {
+            throw(
+                (VpdJsonException("Failed to access Json path", jsonToParse)));
+        }
+
+        try
+        {
+            js = json::parse(inventoryJson);
+        }
+        catch (json::parse_error& ex)
+        {
+            throw((VpdJsonException("Json parsing failed", jsonToParse)));
+        }
 
         if ((js.find("frus") == js.end()) ||
             (js["frus"].find(file) == js["frus"].end()))
@@ -598,6 +622,8 @@
             return 0;
         }
 
+        baseFruInventoryPath = js["frus"][file][0]["inventoryPath"];
+
         Binary vpdVector = getVpdDataInVector(js, file);
         ParserInterface* parser = ParserFactory::getParser(move(vpdVector));
 
@@ -616,6 +642,35 @@
         // release the parser object
         ParserFactory::freeParser(parser);
     }
+    catch (const VpdJsonException& ex)
+    {
+        additionalData.emplace("JSON_PATH", ex.getJsonPath());
+        additionalData.emplace("DESCRIPTION", ex.what());
+        createPEL(additionalData, errIntfForJsonFailure);
+
+        cerr << ex.what() << "\n";
+        rc = -1;
+    }
+    catch (const VpdEccException& ex)
+    {
+        additionalData.emplace("DESCRIPTION", "ECC check failed");
+        additionalData.emplace("CALLOUT_INVENTORY_PATH",
+                               INVENTORY_PATH + baseFruInventoryPath);
+        createPEL(additionalData, errIntfForEccCheckFail);
+
+        cerr << ex.what() << "\n";
+        rc = -1;
+    }
+    catch (const VpdDataException& ex)
+    {
+        additionalData.emplace("DESCRIPTION", "Invalid VPD data");
+        additionalData.emplace("CALLOUT_INVENTORY_PATH",
+                               INVENTORY_PATH + baseFruInventoryPath);
+        createPEL(additionalData, errIntfForInvalidVPD);
+
+        cerr << ex.what() << "\n";
+        rc = -1;
+    }
     catch (exception& e)
     {
         cerr << e.what() << "\n";
diff --git a/impl.cpp b/impl.cpp
index b64ba89..d8a6138 100644
--- a/impl.cpp
+++ b/impl.cpp
@@ -485,37 +485,24 @@
 
 Store Impl::run()
 {
-    try
-    {
-        // Check if the VHDR record is present
-        checkHeader();
+    // Check if the VHDR record is present
+    checkHeader();
 
-        auto iterator = vpd.cbegin();
+    auto iterator = vpd.cbegin();
 
-        // Read the table of contents record
-        std::size_t ptLen = readTOC(iterator);
+    // Read the table of contents record
+    std::size_t ptLen = readTOC(iterator);
 
-        // Read the table of contents record, to get offsets
-        // to other records.
-        auto offsets = readPT(iterator, ptLen);
-        for (const auto& offset : offsets)
-        {
-            processRecord(offset);
-        }
-        // Return a Store object, which has interfaces to
-        // access parsed VPD by record:keyword
-        return Store(std::move(out));
-    }
-    catch (const VpdEccException& ex)
+    // Read the table of contents record, to get offsets
+    // to other records.
+    auto offsets = readPT(iterator, ptLen);
+    for (const auto& offset : offsets)
     {
-        // TODO: Create PEL
-        throw std::runtime_error(ex.what());
+        processRecord(offset);
     }
-    catch (const VpdDataException& ex)
-    {
-        // TODO: Create PEL
-        throw std::runtime_error(ex.what());
-    }
+    // Return a Store object, which has interfaces to
+    // access parsed VPD by record:keyword
+    return Store(std::move(out));
 }
 
 void Impl::checkVPDHeader()
diff --git a/types.hpp b/types.hpp
index c80af32..5f24bd0 100644
--- a/types.hpp
+++ b/types.hpp
@@ -47,6 +47,7 @@
 using systemType = std::string;
 using deviceTree = std::string;
 using deviceTreeMap = std::unordered_map<systemType, deviceTree>;
+using PelAdditionalData = std::map<std::string, std::string>;
 
 } // namespace inventory
 
diff --git a/utils.cpp b/utils.cpp
index 24d7b4c..8b33305 100644
--- a/utils.cpp
+++ b/utils.cpp
@@ -12,31 +12,38 @@
 namespace vpd
 {
 using namespace openpower::vpd::constants;
+using namespace inventory;
+using namespace phosphor::logging;
+
 namespace inventory
 {
 
-auto getPIMService()
+std::string getService(sdbusplus::bus::bus& bus, const std::string& path,
+                       const std::string& interface)
 {
-    auto bus = sdbusplus::bus::new_default();
-    auto mapper =
-        bus.new_method_call("xyz.openbmc_project.ObjectMapper",
-                            "/xyz/openbmc_project/object_mapper",
-                            "xyz.openbmc_project.ObjectMapper", "GetObject");
-
-    mapper.append(pimPath);
-    mapper.append(std::vector<std::string>({pimIntf}));
-
-    auto result = bus.call(mapper);
-    if (result.is_method_error())
-    {
-        throw std::runtime_error("ObjectMapper GetObject failed");
-    }
+    auto mapper = bus.new_method_call(mapperDestination, mapperObjectPath,
+                                      mapperInterface, "GetObject");
+    mapper.append(path, std::vector<std::string>({interface}));
 
     std::map<std::string, std::vector<std::string>> response;
-    result.read(response);
+    try
+    {
+        auto reply = bus.call(mapper);
+        reply.read(response);
+    }
+    catch (const sdbusplus::exception::SdBusError& e)
+    {
+        log<level::ERR>("D-Bus call exception",
+                        entry("OBJPATH=%s", mapperObjectPath),
+                        entry("INTERFACE=%s", mapperInterface),
+                        entry("EXCEPTION=%s", e.what()));
+
+        throw std::runtime_error("Service name is not found");
+    }
+
     if (response.empty())
     {
-        throw std::runtime_error("ObjectMapper GetObject bad response");
+        throw std::runtime_error("Service name response is empty");
     }
 
     return response.begin()->first;
@@ -44,12 +51,10 @@
 
 void callPIM(ObjectMap&& objects)
 {
-    std::string service;
-
     try
     {
-        service = getPIMService();
         auto bus = sdbusplus::bus::new_default();
+        auto service = getService(bus, pimPath, pimIntf);
         auto pimMsg =
             bus.new_method_call(service.c_str(), pimPath, pimIntf, "Notify");
         pimMsg.append(std::move(objects));
@@ -174,5 +179,27 @@
     }
     return propVal;
 }
+
+void createPEL(const std::map<std::string, std::string>& additionalData,
+               const std::string& errIntf)
+{
+    try
+    {
+        auto bus = sdbusplus::bus::new_default();
+
+        auto service = getService(bus, loggerObjectPath, loggerCreateInterface);
+        auto method = bus.new_method_call(service.c_str(), loggerObjectPath,
+                                          loggerCreateInterface, "Create");
+
+        method.append(errIntf, "xyz.openbmc_project.Logging.Entry.Level.Error",
+                      additionalData);
+        auto resp = bus.call(method);
+    }
+    catch (const sdbusplus::exception::SdBusError& e)
+    {
+        throw std::runtime_error(
+            "Error in invoking D-Bus logging create interface to register PEL");
+    }
+}
 } // namespace vpd
 } // namespace openpower
diff --git a/utils.hpp b/utils.hpp
index 54e562c..d531842 100644
--- a/utils.hpp
+++ b/utils.hpp
@@ -79,5 +79,13 @@
  */
 string readBusProperty(const string& obj, const string& inf,
                        const string& prop);
+
+/**
+ * @brief API to create PEL entry
+ * @param[in] Map holding the additional data
+ * @param[in] error interface
+ */
+void createPEL(const std::map<std::string, std::string>& additionalData,
+               const std::string& errIntf);
 } // namespace vpd
 } // namespace openpower