Create PEL for boot errors with error XML data and traces

Add callback method into boot library to receive
status of boot load.

Add callback method into PHAL library to get back
traces logged so that the same can be used in
creation of PEL during failure.

Tested:
"User Data 1": {
    "Section Version": "1",
    "Sub-section type": "1",
    "Created by": "0x2000",
    "CBS_CS_IDLE_VALUE": "00000002",
    "CBS_CS_READ": "84000002",
    "HWP Error description": "CBS did not complete (did not arrive in IDLE
state) within timeout",
    "HW_DELAY": "0009c400",
    "HwpReturnCode": "RC_CBS_NOT_IN_IDLE_STATE",
    "LOG000 2020-03-08 06:26:34": "inf: p10_start_cbs:58 ",
    "LOG001 2020-03-08 06:26:34": "p10_start_cbs: Entering ...",
    "LOG002 2020-03-08 06:26:34": "err: CBS_NOT_IN_IDLE_STATE:36720 ",
    "LOG003 2020-03-08 06:26:34": "CBS did not complete (did not arrive in IDLE
state) within timeout",
    "LOG004 2020-03-08 06:26:34": "err: _setHwpError:114 ",
    "LOG005 2020-03-08 06:26:34": "_setHwpError: Creating HWP error 0xa97f5",
    "LOG006 2020-03-08 06:26:34": "err: p10_start_cbs:119 ",
    "LOG007 2020-03-08 06:26:34": "ERROR: CBS HAS NOT REACHED IDLE STATE VALUE
0x002 ",
    "LOOP_COUNT": "000000c8",
    "_PID": "495"
}

Signed-off-by: Marri Devender Rao <devenrao@in.ibm.com>
Change-Id: I06491f09041edcd16d8db5890c74a4db8d08e920
diff --git a/Makefile.am b/Makefile.am
index 3b1693b..d1842cb 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -46,6 +46,13 @@
 openpower_proc_nmi_CXXFLAGS = $(PHOSPHOR_LOGGING_CFLAGS) \
                               $(PHOSPHOR_DBUS_INTERFACES_CFLAGS) \
                               $(SDBUSPLUS_CFLAGS)
+if ENABLE_PHAL
+noinst_LTLIBRARIES = libphalerror.la
+libphalerror_la_SOURCES = phalerror/create_pel.cpp \
+            phalerror/phal_error.cpp
+
+openpower_proc_control_LDADD = libphalerror.la
+endif
 
 SUBDIRS = test
 
diff --git a/configure.ac b/configure.ac
index dafe28b..887cc54 100644
--- a/configure.ac
+++ b/configure.ac
@@ -6,6 +6,7 @@
 
 # Checks for programs
 AC_PROG_CXX
+AM_PROG_AR
 AC_PROG_INSTALL #Checks/sets the install variable to be used
 AC_PROG_MAKE_SET
 
@@ -54,22 +55,30 @@
 
 AS_IF([test "$enable_phal" == "yes"],
       [
-       AX_ABSOLUTE_HEADER([libipl.h])
-       if test x"$gl_cv_absolute_libipl_h" == "x" ; then
-               AC_MSG_ERROR([Cannot find libipl.h path])
+       AX_ABSOLUTE_HEADER([libipl.H])
+       if test x"$gl_cv_absolute_libipl_H" == "x" ; then
+               AC_MSG_ERROR([Cannot find libipl.H path])
        fi
-
        AC_CHECK_LIB([ipl], [ipl_init])
        if test x"$ac_cv_lib_ipl_ipl_init" != "xyes" ; then
                AC_MSG_ERROR([IPL library not found])
        fi
 
+       AX_ABSOLUTE_HEADER([libekb.H])
+       if test x"$gl_cv_absolute_libekb_H" == "x" ; then
+               AC_MSG_ERROR([Cannot find libekb.H path])
+       fi
+       AC_CHECK_LIB([ekb], [libekb_init])
+       if test x"$ac_cv_lib_ekb_libekb_init" != "xyes" ; then
+               AC_MSG_ERROR([EKB library not found])
+       fi
+
        CHIPS+=" phal common"
        AC_CONFIG_FILES([set-spi-mux.service])
       ]
      )
-AM_CONDITIONAL([ENABLE_PHAL], [test "$enable_phal" == "yes"])
 
+AM_CONDITIONAL([ENABLE_PHAL], [test "$enable_phal" == "yes"])
 AS_IF([test "x$CHIPS" == "x"], [CHIPS="p9 openfsi common"])
 
 AC_CONFIG_FILES([Makefile.generated],
diff --git a/phalerror/create_pel.cpp b/phalerror/create_pel.cpp
new file mode 100644
index 0000000..3a047d3
--- /dev/null
+++ b/phalerror/create_pel.cpp
@@ -0,0 +1,89 @@
+#include "create_pel.hpp"

+

+#include <libekb.H>

+#include <unistd.h>

+

+#include <map>

+#include <phosphor-logging/elog.hpp>

+#include <xyz/openbmc_project/Logging/Entry/server.hpp>

+namespace openpower

+{

+using namespace phosphor::logging;

+

+namespace util

+{

+std::string getService(sdbusplus::bus::bus& bus, const std::string& objectPath,

+                       const std::string& interface)

+{

+    constexpr auto mapperBusBame = "xyz.openbmc_project.ObjectMapper";

+    constexpr auto mapperObjectPath = "/xyz/openbmc_project/object_mapper";

+    constexpr auto mapperInterface = "xyz.openbmc_project.ObjectMapper";

+    std::vector<std::pair<std::string, std::vector<std::string>>> response;

+    auto method = bus.new_method_call(mapperBusBame, mapperObjectPath,

+                                      mapperInterface, "GetObject");

+    method.append(objectPath, std::vector<std::string>({interface}));

+    try

+    {

+        auto reply = bus.call(method);

+        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("Service name response is empty");

+    }

+    return response.begin()->first;

+}

+} // namespace util

+

+namespace pel

+{

+void createBootErrorPEL(const FFDCData& ffdcData)

+{

+    constexpr auto loggingObjectPath = "/xyz/openbmc_project/logging";

+    constexpr auto loggingInterface = "xyz.openbmc_project.Logging.Create";

+

+    std::map<std::string, std::string> additionalData;

+    auto bus = sdbusplus::bus::new_default();

+    additionalData.emplace("_PID", std::to_string(getpid()));

+    for (auto& data : ffdcData)

+    {

+        additionalData.emplace(data);

+    }

+    try

+    {

+        static constexpr auto bootErrorMessage =

+            "org.open_power.PHAL.Error.Boot";

+        std::string service =

+            util::getService(bus, loggingObjectPath, loggingInterface);

+        auto method = bus.new_method_call(service.c_str(), loggingObjectPath,

+                                          loggingInterface, "Create");

+        auto level =

+            sdbusplus::xyz::openbmc_project::Logging::server::convertForMessage(

+                sdbusplus::xyz::openbmc_project::Logging::server::Entry::Level::

+                    Error);

+        method.append(bootErrorMessage, level, additionalData);

+        auto resp = bus.call(method);

+    }

+    catch (const sdbusplus::exception::SdBusError& e)

+    {

+        log<level::ERR>("D-Bus call exception",

+                        entry("OBJPATH=%s", loggingObjectPath),

+                        entry("INTERFACE=%s", loggingInterface),

+                        entry("EXCEPTION=%s", e.what()));

+

+        throw std::runtime_error(

+            "Error in invoking D-Bus logging create interface");

+    }

+}

+} // namespace pel

+} // namespace openpower

diff --git a/phalerror/create_pel.hpp b/phalerror/create_pel.hpp
new file mode 100644
index 0000000..b80b642
--- /dev/null
+++ b/phalerror/create_pel.hpp
@@ -0,0 +1,33 @@
+#pragma once

+

+#include <sdbusplus/bus.hpp>

+#include <string>

+#include <vector>

+namespace openpower

+{

+namespace util

+{

+/**

+ * Get D-Bus service name for the specified object and interface

+ *

+ * @param[in] bus - sdbusplus D-Bus to attach to

+ * @param[in] objectPath - D-Bus object path

+ * @param[in] interface - D-Bus interface name

+ *

+ * @return service name on success and exception on failure

+ */

+std::string getService(sdbusplus::bus::bus& bus, const std::string& objectPath,

+                       const std::string& interface);

+} // namespace util

+namespace pel

+{

+using FFDCData = std::vector<std::pair<std::string, std::string>>;

+/**

+ * Create boot error PEL

+ *

+ * @param[in] ffdcData - failure data to append to PEL

+ */

+void createBootErrorPEL(const FFDCData& ffdcData);

+

+} // namespace pel

+} // namespace openpower

diff --git a/phalerror/phal_error.cpp b/phalerror/phal_error.cpp
new file mode 100644
index 0000000..4f33ff2
--- /dev/null
+++ b/phalerror/phal_error.cpp
@@ -0,0 +1,101 @@
+#include "phal_error.hpp"

+

+#include "create_pel.hpp"

+

+#include <libekb.H>

+#include <libipl.H>

+

+#include <iomanip>

+#include <phosphor-logging/elog.hpp>

+#include <sstream>

+

+namespace openpower

+{

+namespace pel

+{

+using namespace phosphor::logging;

+

+namespace detail

+{

+

+// keys need to be unique so using counter value to generate unique key

+static int counter = 0;

+

+// list of debug traces

+static std::vector<std::pair<std::string, std::string>> traceLog;

+

+void processLogTraceCallback(void* private_data, const char* fmt, va_list ap)

+{

+    va_list vap;

+    va_copy(vap, ap);

+    std::vector<char> log(1 + std::vsnprintf(nullptr, 0, fmt, ap));

+    std::vsnprintf(log.data(), log.size(), fmt, vap);

+    va_end(vap);

+    std::string logstr(log.begin(), log.end());

+

+    // ignore stray characters in log traces coming from ekb

+    // example: "\n", which are good for print to screen

+    if (logstr.length() < 5)

+    {

+        return;

+    }

+

+    char timeBuf[80];

+    time_t t = time(0);

+    tm myTm{};

+    gmtime_r(&t, &myTm);

+    strftime(timeBuf, 80, "%Y-%m-%d %H:%M:%S", &myTm);

+

+    // key values need to be unique for PEL

+    // TODO #openbmc/dev/issues/1563

+    // If written to Json no need to worry about unique KEY

+    std::stringstream str;

+    str << std::setfill('0');

+    str << "LOG" << std::setw(3) << counter;

+    str << " " << timeBuf;

+    traceLog.emplace_back(std::make_pair(str.str(), std::move(logstr)));

+    counter++;

+}

+

+void processBootErrorCallback(bool status)

+{

+    log<level::INFO>("processBootCallback ", entry("STATUS=%d", status));

+    try

+    {

+        // If failure in hwp execution

+        if (!status)

+        {

+            FFDCData ffdc = libekb_get_ffdc();

+            ffdc.insert(ffdc.end(), traceLog.begin(), traceLog.end());

+            openpower::pel::createBootErrorPEL(ffdc);

+        }

+    }

+    catch (std::exception& ex)

+    {

+        reset();

+        throw ex;

+    }

+    reset();

+}

+

+void reset()

+{

+    // reset the trace log and counter

+    traceLog.clear();

+    counter = 0;

+}

+} // namespace detail

+

+void addBootErrorCallbacks()

+{

+    // set log level to info

+    ipl_set_loglevel(IPL_INFO);

+

+    // add callback for debug traces

+    ipl_set_logfunc(detail::processLogTraceCallback, NULL);

+

+    // add callback for ipl failures

+    ipl_set_app_callback_func(detail::processBootErrorCallback);

+}

+} // namespace pel

+} // namespace openpower

diff --git a/phalerror/phal_error.hpp b/phalerror/phal_error.hpp
new file mode 100644
index 0000000..055cf35
--- /dev/null
+++ b/phalerror/phal_error.hpp
@@ -0,0 +1,49 @@
+#pragma once

+

+#include <cstdarg>

+namespace openpower

+{

+namespace pel

+{

+namespace detail

+{

+

+/**

+ * @brief Process debug traces

+ *

+ * Function adds debug traces to the list so that it will be added to the

+ * PEL upon failure

+ *

+ * @param[in] private_data - pointer to private data, unused now

+ * @param[in] fmt - format for variable list arguments

+ * @param[in] ap - object of va_list, holds information needed to retrieve

+ *                 the additional arguments

+ */

+

+void processLogTraceCallback(void* private_data, const char* fmt, va_list ap);

+

+/**

+ * @brief Process boot failure/success status

+ *

+ * If status is success log traces are cleared else used in the

+ * creation of failure

+ *

+ * @param[in] status - Boot execution status

+ */

+void processBootErrorCallback(bool status);

+

+/**

+ * @brief Reset trace log list

+ */

+void reset();

+} // namespace detail

+

+/**

+ * @brief Add callbacks for debug traces and boot errors

+ *

+ * This function adds callback for debug traces and for boot

+ * errors

+ */

+void addBootErrorCallbacks();

+} // namespace pel

+} // namespace openpower

diff --git a/proc_control.cpp b/proc_control.cpp
index 1afdace..3bdce8b 100644
--- a/proc_control.cpp
+++ b/proc_control.cpp
@@ -104,10 +104,9 @@
         commit<fsi_error::SlaveDetectionFailure>();
         return -1;
     }
-    // TODO ibm-openbmc#1470
-    catch (common_error::InternalFailure& e)
+    catch (std::exception& e)
     {
-        commit<common_error::InternalFailure>();
+        log<level::ERR>("exception raised", entry("EXCEPTION=%s", e.what()));
         return -1;
     }
 
diff --git a/procedures/phal/start_host.cpp b/procedures/phal/start_host.cpp
index c828309..a5e7bea 100644
--- a/procedures/phal/start_host.cpp
+++ b/procedures/phal/start_host.cpp
@@ -1,10 +1,7 @@
-extern "C" {
-#include <libipl.h>
-}
+#include "phalerror/phal_error.hpp"
 
-#include "xyz/openbmc_project/Common/error.hpp"
+#include <libipl.H>
 
-#include <phosphor-logging/elog-errors.hpp>
 #include <phosphor-logging/log.hpp>
 #include <registration.hpp>
 namespace openpower
@@ -13,7 +10,6 @@
 {
 
 using namespace phosphor::logging;
-using namespace sdbusplus::xyz::openbmc_project::Common::Error;
 
 /**
  * @brief Starts the self boot engine on POWER processor position 0
@@ -22,18 +18,22 @@
  */
 void startHost()
 {
+    // add callback methods for debug traces and for boot failures
+    openpower::pel::addBootErrorCallbacks();
+
+    // callback method will be called upon failure which will create the PEL
     if (ipl_init() != 0)
     {
         log<level::ERR>("ipl_init failed");
-        // TODO ibm-openbmc#1470
-        elog<InternalFailure>();
+        throw std::runtime_error("Boot initialization failed");
     }
 
-    if (ipl_run_major(0) > 0)
+    // callback method will be called upon failure which will create the PEL
+    int rc = ipl_run_major(0);
+    if (rc > 0)
     {
         log<level::ERR>("step 0 failed to start the host");
-        // TODO ibm-openbmc#1470
-        elog<InternalFailure>();
+        throw std::runtime_error("Failed to execute host start boot step");
     }
 }