Add PEL creation for SBE chip-op failures

This commit introduces a new function within the SBE dump collection
mechanism to create PELs) when chip operations on SBE fail.

Test:
Make sure the PELs are logged during chip-op failure

Signed-off-by: Dhruvaraj Subhashchandran <dhruvaraj@in.ibm.com>
Change-Id: I23f78b702f4a2b9cf01315c3fbb487dc428bd2a0
diff --git a/dump/create_pel.cpp b/dump/create_pel.cpp
new file mode 100644
index 0000000..916d312
--- /dev/null
+++ b/dump/create_pel.cpp
@@ -0,0 +1,191 @@
+#include "create_pel.hpp"
+
+#include "dump_utils.hpp"
+
+#include <fcntl.h>
+#include <libekb.H>
+#include <unistd.h>
+
+#include <phosphor-logging/elog.hpp>
+#include <phosphor-logging/lg2.hpp>
+#include <xyz/openbmc_project/Logging/Create/server.hpp>
+#include <xyz/openbmc_project/Logging/Entry/server.hpp>
+
+#include <cerrno>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <format>
+#include <map>
+#include <stdexcept>
+#include <string>
+#include <tuple>
+#include <vector>
+
+namespace openpower::dump::pel
+{
+
+using namespace phosphor::logging;
+constexpr auto loggingObjectPath = "/xyz/openbmc_project/logging";
+constexpr auto loggingInterface = "xyz.openbmc_project.Logging.Create";
+constexpr auto opLoggingInterface = "org.open_power.Logging.PEL";
+
+uint32_t createSbeErrorPEL(const std::string& event, const sbeError_t& sbeError,
+                           const FFDCData& ffdcData)
+{
+    uint32_t plid = 0;
+    std::unordered_map<std::string, std::string> additionalData = {
+        {"_PID", std::to_string(getpid())}, {"SBE_ERR_MSG", sbeError.what()}};
+    auto bus = sdbusplus::bus::new_default();
+
+    additionalData.emplace("_PID", std::to_string(getpid()));
+    additionalData.emplace("SBE_ERR_MSG", sbeError.what());
+
+    for (auto& data : ffdcData)
+    {
+        additionalData.emplace(data);
+    }
+
+    std::vector<std::tuple<
+        sdbusplus::xyz::openbmc_project::Logging::server::Create::FFDCFormat,
+        uint8_t, uint8_t, sdbusplus::message::unix_fd>>
+        pelFFDCInfo;
+
+    // get SBE ffdc file descriptor
+    auto fd = sbeError.getFd();
+
+    // Negative fd value indicates error case or invalid file
+    // No need of special processing , just log error with additional ffdc.
+    if (fd > 0)
+    {
+        // Refer phosphor-logging/extensions/openpower-pels/README.md section
+        // "Self Boot Engine(SBE) First Failure Data Capture(FFDC) Support"
+        // for details of related to createPEL with SBE FFDC information
+        // usin g CreateWithFFDCFiles api.
+        pelFFDCInfo.emplace_back(
+            std::make_tuple(sdbusplus::xyz::openbmc_project::Logging::server::
+                                Create::FFDCFormat::Custom,
+                            static_cast<uint8_t>(0xCB),
+                            static_cast<uint8_t>(0x01), sbeError.getFd()));
+    }
+    try
+    {
+        auto service = util::getService(bus, opLoggingInterface,
+                                        loggingObjectPath);
+        auto method = bus.new_method_call(service.c_str(), loggingObjectPath,
+                                          opLoggingInterface,
+                                          "CreatePELWithFFDCFiles");
+        auto level =
+            sdbusplus::xyz::openbmc_project::Logging::server::convertForMessage(
+                sdbusplus::xyz::openbmc_project::Logging::server::Entry::Level::
+                    Error);
+        method.append(event, level, additionalData, pelFFDCInfo);
+        auto response = bus.call(method);
+
+        // reply will be tuple containing bmc log id, platform log id
+        std::tuple<uint32_t, uint32_t> reply = {0, 0};
+
+        // parse dbus response into reply
+        response.read(reply);
+        plid = std::get<1>(reply); // platform log id is tuple "second"
+    }
+    catch (const sdbusplus::exception::exception& e)
+    {
+        lg2::error(
+            "D-Bus call exception OBJPATH={OBJPATH}, INTERFACE={INTERFACE}, "
+            "EXCEPTION={ERROR}",
+            "OBJPATH", loggingObjectPath, "INTERFACE", loggingInterface,
+            "ERROR", e);
+
+        throw;
+    }
+    catch (const std::exception& e)
+    {
+        throw;
+    }
+
+    return plid;
+}
+
+FFDCFile::FFDCFile(const json& pHALCalloutData) :
+    calloutData(pHALCalloutData.dump()),
+    calloutFile("/tmp/phalPELCalloutsJson.XXXXXX"), fileFD(-1)
+{
+    prepareFFDCFile();
+}
+
+FFDCFile::~FFDCFile()
+{
+    removeCalloutFile();
+}
+
+int FFDCFile::getFileFD() const
+{
+    return fileFD;
+}
+
+void FFDCFile::prepareFFDCFile()
+{
+    createCalloutFile();
+    writeCalloutData();
+    setCalloutFileSeekPos();
+}
+
+void FFDCFile::createCalloutFile()
+{
+    fileFD = mkostemp(const_cast<char*>(calloutFile.c_str()), O_RDWR);
+
+    if (fileFD == -1)
+    {
+        lg2::error("Failed to create phalPELCallouts file({FILE}), "
+                   "errorno({ERRNO}) and errormsg({ERRORMSG})",
+                   "FILE", calloutFile, "ERRNO", errno, "ERRORMSG",
+                   strerror(errno));
+        throw std::runtime_error("Failed to create phalPELCallouts file");
+    }
+}
+
+void FFDCFile::writeCalloutData()
+{
+    ssize_t rc = write(fileFD, calloutData.c_str(), calloutData.size());
+
+    if (rc == -1)
+    {
+        lg2::error("Failed to write phaPELCallout info in file({FILE}), "
+                   "errorno({ERRNO}), errormsg({ERRORMSG})",
+                   "FILE", calloutFile, "ERRNO", errno, "ERRORMSG",
+                   strerror(errno));
+        throw std::runtime_error("Failed to write phalPELCallouts info");
+    }
+    else if (rc != static_cast<ssize_t>(calloutData.size()))
+    {
+        lg2::warning("Could not write all phal callout info in file({FILE}), "
+                     "written byte({WRITTEN}), total byte({TOTAL})",
+                     "FILE", calloutFile, "WRITTEN", rc, "TOTAL",
+                     calloutData.size());
+    }
+}
+
+void FFDCFile::setCalloutFileSeekPos()
+{
+    int rc = lseek(fileFD, 0, SEEK_SET);
+
+    if (rc == -1)
+    {
+        lg2::error("Failed to set SEEK_SET for phalPELCallouts in "
+                   "file({FILE}), errorno({ERRNO}), errormsg({ERRORMSG})",
+                   "FILE", calloutFile, "ERRNO", errno, "ERRORMSG",
+                   strerror(errno));
+
+        throw std::runtime_error(
+            "Failed to set SEEK_SET for phalPELCallouts file");
+    }
+}
+
+void FFDCFile::removeCalloutFile()
+{
+    close(fileFD);
+    std::remove(calloutFile.c_str());
+}
+
+} // namespace openpower::dump::pel