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
diff --git a/dump/create_pel.hpp b/dump/create_pel.hpp
new file mode 100644
index 0000000..13d30ff
--- /dev/null
+++ b/dump/create_pel.hpp
@@ -0,0 +1,114 @@
+#pragma once
+
+#include <phal_exception.H>
+
+#include <nlohmann/json.hpp>
+
+#include <string>
+#include <vector>
+namespace openpower::dump::pel
+{
+
+using FFDCData = std::vector<std::pair<std::string, std::string>>;
+
+using json = nlohmann::json;
+
+using namespace openpower::phal;
+
+/**
+ * @brief Create SBE boot error PEL and return id
+ *
+ * @param[in] event - the event type
+ * @param[in] sbeError - SBE error object
+ * @param[in] ffdcData - failure data to append to PEL
+ * @return Platform log id
+ */
+uint32_t createSbeErrorPEL(const std::string& event, const sbeError_t& sbeError,
+ const FFDCData& ffdcData);
+
+/**
+ * @class FFDCFile
+ * @brief This class is used to create ffdc data file and to get fd
+ */
+class FFDCFile
+{
+ public:
+ FFDCFile() = delete;
+ FFDCFile(const FFDCFile&) = delete;
+ FFDCFile& operator=(const FFDCFile&) = delete;
+ FFDCFile(FFDCFile&&) = delete;
+ FFDCFile& operator=(FFDCFile&&) = delete;
+
+ /**
+ * Used to pass json object to create unique ffdc file by using
+ * passed json data.
+ */
+ explicit FFDCFile(const json& pHALCalloutData);
+
+ /**
+ * Used to remove created ffdc file.
+ */
+ ~FFDCFile();
+
+ /**
+ * Used to get created ffdc file file descriptor id.
+ *
+ * @return file descriptor id
+ */
+ int getFileFD() const;
+
+ private:
+ /**
+ * Used to store callout ffdc data from passed json object.
+ */
+ std::string calloutData;
+
+ /**
+ * Used to store unique ffdc file name.
+ */
+ std::string calloutFile;
+
+ /**
+ * Used to store created ffdc file descriptor id.
+ */
+ int fileFD;
+
+ /**
+ * Used to create ffdc file to pass PEL api for creating
+ * pel records.
+ *
+ * @return NULL
+ */
+ void prepareFFDCFile();
+
+ /**
+ * Create unique ffdc file.
+ *
+ * @return NULL
+ */
+ void createCalloutFile();
+
+ /**
+ * Used write json object value into created file.
+ *
+ * @return NULL
+ */
+ void writeCalloutData();
+
+ /**
+ * Used set ffdc file seek position begining to consume by PEL
+ *
+ * @return NULL
+ */
+ void setCalloutFileSeekPos();
+
+ /**
+ * Used to remove created ffdc file.
+ *
+ * @return NULL
+ */
+ void removeCalloutFile();
+
+}; // FFDCFile end
+
+} // namespace openpower::dump::pel
diff --git a/dump/meson.build b/dump/meson.build
index 45140d0..586b43e 100644
--- a/dump/meson.build
+++ b/dump/meson.build
@@ -16,6 +16,8 @@
'sbe_dump_collector.cpp',
'dump_collect_main.cpp',
'dump_utils.cpp',
+ 'create_pel.cpp',
+ 'sbe_type.cpp',
)
executable('dump-collect',
diff --git a/dump/sbe_consts.hpp b/dump/sbe_consts.hpp
index 9fea1f0..d59a698 100644
--- a/dump/sbe_consts.hpp
+++ b/dump/sbe_consts.hpp
@@ -15,4 +15,9 @@
// Collect the dumps with clock off
constexpr auto SBE_CLOCK_OFF = 0x2;
+// Dump command class
+constexpr auto SBEFIFO_CMD_CLASS_DUMP = 0xAA00;
+
+// Get dump method
+constexpr auto SBEFIFO_CMD_GET_DUMP = 0x01;
} // namespace openpower::dump::SBE
diff --git a/dump/sbe_dump_collector.cpp b/dump/sbe_dump_collector.cpp
index 119fdff..58a34e7 100644
--- a/dump/sbe_dump_collector.cpp
+++ b/dump/sbe_dump_collector.cpp
@@ -4,12 +4,13 @@
#include <libpdbg_sbe.h>
}
+#include "create_pel.hpp"
#include "sbe_consts.hpp"
#include "sbe_dump_collector.hpp"
+#include "sbe_type.hpp"
#include <libphal.H>
-#include <sys/wait.h>
-#include <unistd.h>
+#include <phal_exception.H>
#include <phosphor-logging/elog-errors.hpp>
#include <phosphor-logging/lg2.hpp>
@@ -130,6 +131,27 @@
return futures;
}
+void SbeDumpCollector::logErrorAndCreatePEL(
+ const openpower::phal::sbeError_t& sbeError, uint64_t chipPos,
+ SBETypes sbeType, uint32_t cmdClass, uint32_t cmdType)
+{
+ try
+ {
+ std::string event = sbeTypeAttributes.at(sbeType).chipOpFailure;
+
+ openpower::dump::pel::FFDCData pelAdditionalData = {
+ {"SRC6", std::format("{:X}{:X}", chipPos, (cmdClass | cmdType))}};
+
+ openpower::dump::pel::createSbeErrorPEL(event, sbeError,
+ pelAdditionalData);
+ }
+ catch (const std::out_of_range& e)
+ {
+ lg2::error("Unknown SBE Type({SBETYPE}) ErrorMsg({ERROR})", "SBETYPE",
+ sbeType, "ERROR", e);
+ }
+}
+
void SbeDumpCollector::collectDumpFromSBE(struct pdbg_target* chip,
const std::filesystem::path& path,
uint32_t id, uint8_t type,
@@ -137,6 +159,8 @@
uint64_t failingUnit)
{
auto chipPos = pdbg_target_index(chip);
+ SBETypes sbeType = getSBEType(chip);
+ auto chipName = sbeTypeAttributes.at(sbeType).chipName;
lg2::info(
"Collecting dump from proc({PROC}): path({PATH}) id({ID}) "
"type({TYPE}) clockState({CLOCKSTATE}) failingUnit({FAILINGUNIT})",
@@ -175,13 +199,14 @@
return;
}
- writeDumpFile(path, id, clockState, 0, "proc", chipPos, dataPtr, len);
+ writeDumpFile(path, id, clockState, 0, chipName, chipPos, dataPtr, len);
}
void SbeDumpCollector::writeDumpFile(
const std::filesystem::path& path, const uint32_t id,
- const uint8_t clockState, const uint8_t nodeNum, std::string chipName,
- const uint8_t chipPos, util::DumpDataPtr& dataPtr, const uint32_t len)
+ const uint8_t clockState, const uint8_t nodeNum,
+ const std::string& chipName, const uint8_t chipPos,
+ util::DumpDataPtr& dataPtr, const uint32_t len)
{
using namespace sdbusplus::xyz::openbmc_project::Common::Error;
namespace fileError = sdbusplus::xyz::openbmc_project::Common::File::Error;
diff --git a/dump/sbe_dump_collector.hpp b/dump/sbe_dump_collector.hpp
index 93ecdd4..2e99b53 100644
--- a/dump/sbe_dump_collector.hpp
+++ b/dump/sbe_dump_collector.hpp
@@ -8,6 +8,9 @@
#include "dump_utils.hpp"
#include "sbe_consts.hpp"
+#include "sbe_type.hpp"
+
+#include <phal_exception.H>
#include <cstdint>
#include <filesystem>
@@ -131,7 +134,7 @@
*/
void writeDumpFile(const std::filesystem::path& path, const uint32_t id,
const uint8_t clockState, const uint8_t nodeNum,
- std::string chipName, const uint8_t chipPos,
+ const std::string& chipName, const uint8_t chipPos,
util::DumpDataPtr& dataPtr, const uint32_t len);
/**
@@ -160,6 +163,33 @@
? 1
: 0;
}
+
+ /**
+ * Logs an error and creates a PEL for SBE chip-op failures.
+ *
+ * @param sbeError - An error object encapsulating details about the SBE
+ * error.
+ * @param chipPos - The position of the chip where the error occurred.
+ * @param sbeType - The type of SBE, used to determine the event log
+ * message.
+ * @param cmdClass - The command class associated with the SBE operation.
+ * @param cmdType - The specific type of command within the command class.
+ *
+ */
+ void logErrorAndCreatePEL(const openpower::phal::sbeError_t& sbeError,
+ uint64_t chipPos, SBETypes sbeType,
+ uint32_t cmdClass, uint32_t cmdType);
+
+ /**
+ * Determines the type of SBE for a given chip target.
+ *
+ * @param chip - A pointer to a pdbg_target structure representing the chip.
+ * @return The SBE type for the given chip target.
+ */
+ inline SBETypes getSBEType([[maybe_unused]] struct pdbg_target* chip)
+ {
+ return SBETypes::PROC;
+ }
};
} // namespace openpower::dump::sbe_chipop
diff --git a/dump/sbe_type.cpp b/dump/sbe_type.cpp
new file mode 100644
index 0000000..41ae7cf
--- /dev/null
+++ b/dump/sbe_type.cpp
@@ -0,0 +1,10 @@
+#include "sbe_type.hpp"
+
+namespace openpower::dump
+{
+
+const std::map<SBETypes, SBEAttributes> sbeTypeAttributes = {
+ {SBETypes::PROC,
+ {"proc", "org.open_power.Processor.Error.SbeChipOpFailure"}}};
+
+} // namespace openpower::dump
diff --git a/dump/sbe_type.hpp b/dump/sbe_type.hpp
new file mode 100644
index 0000000..e46d030
--- /dev/null
+++ b/dump/sbe_type.hpp
@@ -0,0 +1,21 @@
+#pragma once
+
+#include <map>
+#include <string>
+
+namespace openpower::dump
+{
+
+enum class SBETypes
+{
+ PROC,
+};
+
+struct SBEAttributes
+{
+ std::string chipName;
+ std::string chipOpFailure;
+};
+
+extern const std::map<SBETypes, SBEAttributes> sbeTypeAttributes;
+} // namespace openpower::dump