Attn: Add support for raw PEL creation
Attention handler needs to pass raw PEL's to phosphor logging in order
to submit PEL's on behalf of other components (e.g. hypervisor)
Signed-off-by: Ben Tyner <ben.tyner@ibm.com>
Change-Id: Id9a30728e7b463ac876b5dca023ca2627a25bb16
diff --git a/attn/attn_logging.cpp b/attn/attn_logging.cpp
index 014fc96..a55ac55 100644
--- a/attn/attn_logging.cpp
+++ b/attn/attn_logging.cpp
@@ -1,21 +1,421 @@
#include <unistd.h>
#include <attn/attn_logging.hpp>
+#include <attn/pel/pel_minimal.hpp>
#include <phosphor-logging/log.hpp>
+
namespace attn
{
-/** @brief journal entry of type INFO using phosphor logging */
+/** @brief Journal entry of type INFO using phosphor logging */
template <>
void trace<INFO>(const char* i_message)
{
phosphor::logging::log<phosphor::logging::level::INFO>(i_message);
}
-/** @brief add an event to the log for PEL generation */
-void event(EventType i_event, std::map<std::string, std::string>& i_additional)
+/** @brief Tuple containing information about ffdc files */
+using FFDCTuple =
+ std::tuple<util::FFDCFormat, uint8_t, uint8_t, sdbusplus::message::unix_fd>;
+
+/** @brief Gather messages from the journal */
+std::vector<std::string> sdjGetMessages(const std::string& field,
+ const std::string& fieldValue,
+ unsigned int max);
+
+/**
+ * Create FFDCTuple objects corresponding to the specified FFDC files.
+ *
+ * The D-Bus method to create an error log requires a vector of tuples to
+ * pass in the FFDC file information.
+ *
+ * @param files - FFDC files
+ * @return vector of FFDCTuple objects
+ */
+std::vector<FFDCTuple>
+ createFFDCTuples(const std::vector<util::FFDCFile>& files)
+{
+ std::vector<FFDCTuple> ffdcTuples{};
+ for (const util::FFDCFile& file : files)
+ {
+ ffdcTuples.emplace_back(
+ file.getFormat(), file.getSubType(), file.getVersion(),
+ sdbusplus::message::unix_fd(file.getFileDescriptor()));
+ }
+
+ return ffdcTuples;
+}
+
+/**
+ * @brief Create an FFDCFile object containing raw data
+ *
+ * Throws an exception if an error occurs.
+ *
+ * @param i_buffer - raw data to add to ffdc faw data file
+ * @param i_size - size of the raw data
+ * @return FFDCFile object
+ */
+util::FFDCFile createFFDCRawFile(void* i_buffer, size_t i_size)
+{
+ util::FFDCFile file{util::FFDCFormat::Custom};
+
+ // Write buffer to file and then reset file description file offset
+ int fd = file.getFileDescriptor();
+ write(fd, static_cast<char*>(i_buffer), i_size);
+ lseek(fd, 0, SEEK_SET);
+
+ return file;
+}
+
+/**
+ * @brief Create an FFDCFile object containing the specified lines of text data
+ *
+ * Throws an exception if an error occurs.
+ *
+ * @param lines - lines of text data to write to file
+ * @return FFDCFile object
+ */
+util::FFDCFile createFFDCTraceFile(const std::vector<std::string>& lines)
+{
+ // Create FFDC file of type Text
+ util::FFDCFile file{util::FFDCFormat::Text};
+ int fd = file.getFileDescriptor();
+
+ // Write FFDC lines to file
+ std::string buffer;
+ for (const std::string& line : lines)
+ {
+ // Copy line to buffer. Add newline if necessary.
+ buffer = line;
+ if (line.empty() || (line.back() != '\n'))
+ {
+ buffer += '\n';
+ }
+
+ // write buffer to file
+ write(fd, buffer.c_str(), buffer.size());
+ }
+
+ // Seek to beginning of file so error logging system can read data
+ lseek(fd, 0, SEEK_SET);
+
+ return file;
+}
+
+/**
+ * Create FDDC files from journal messages of relevant executables
+ *
+ * Parse the system journal looking for log entries created by the executables
+ * of interest for logging. For each of these entries create a ffdc trace file
+ * that will be used to create ffdc log entries. These files will be pushed
+ * onto the stack of ffdc files.
+ *
+ * @param i_files - vector of ffdc files that will become log entries
+ */
+void createFFDCTraceFiles(std::vector<util::FFDCFile>& i_files)
+{
+ // Executables of interest
+ std::vector<std::string> executables{"openpower-hw-diags"};
+
+ for (const std::string& executable : executables)
+ {
+ try
+ {
+ // get journal messages
+ std::vector<std::string> messages =
+ sdjGetMessages("SYSLOG_IDENTIFIER", executable, 30);
+
+ // Create FFDC file containing the journal messages
+ if (!messages.empty())
+ {
+ i_files.emplace_back(createFFDCTraceFile(messages));
+ }
+ }
+ catch (const std::exception& e)
+ {
+ std::stringstream ss;
+ ss << "createFFDCFiles: " << e.what();
+ trace<level::INFO>(ss.str().c_str());
+ }
+ }
+}
+
+/**
+ * Create FFDCFile objects containing debug data to store in the error log.
+ *
+ * If an error occurs, the error is written to the journal but an exception
+ * is not thrown.
+ *
+ * @param i_buffer - raw data (if creating raw dump ffdc entry in log)
+ * @return vector of FFDCFile objects
+ */
+std::vector<util::FFDCFile> createFFDCFiles(char* i_buffer = nullptr,
+ size_t i_size = 0)
+{
+ std::vector<util::FFDCFile> files{};
+
+ // Create raw dump file
+ if ((nullptr != i_buffer) && (0 != i_size))
+ {
+ files.emplace_back(createFFDCRawFile(i_buffer, i_size));
+ }
+
+ // Create trace dump file
+ createFFDCTraceFiles(files);
+
+ return files;
+}
+
+/**
+ * Get file descriptor of exisitng PEL
+ *
+ * The backend logging code will search for a PEL having the provided PEL ID
+ * and return a file descriptor to a file containing this PEL's raw PEL data.
+ *
+ * @param i_pelid - the PEL ID
+ * @return file descriptor of file containing the raw PEL data
+ */
+int getPelFd(uint32_t i_pelId)
+{
+ // GetPEL returns file descriptor (int)
+ int fd = -1;
+
+ // Sdbus call specifics
+ constexpr auto service = "xyz.openbmc_project.Logging";
+ constexpr auto path = "/xyz/openbmc_project/logging";
+ constexpr auto interface = "org.open_power.Logging.PEL";
+ constexpr auto function = "GetPEL";
+
+ // Get the PEL file descriptor
+ try
+ {
+ auto bus = sdbusplus::bus::new_default_system();
+ auto method = bus.new_method_call(service, path, interface, function);
+ method.append(i_pelId);
+
+ auto resp = bus.call(method);
+
+ sdbusplus::message::unix_fd msgFd;
+ resp.read(msgFd);
+ fd = dup(msgFd); // -1 if not found
+ }
+ catch (const sdbusplus::exception::SdBusError& e)
+ {
+ std::stringstream ss;
+ ss << "getPelFd: " << e.what();
+ trace<level::INFO>(ss.str().c_str());
+ }
+
+ // File descriptor or -1 if not found or call failed
+ return fd;
+}
+
+/**
+ * Create a PEL for the specified event type
+ *
+ * The additional data provided in the map will be placed in a user data
+ * section of the PEL and may additionally contain key words to trigger
+ * certain behaviors by the backend logging code. Each set of data described
+ * in the vector of ffdc data will be placed in additional user data sections.
+ *
+ * @param i_event - the event type
+ * @param i_additional - map of additional data
+ * @param 9_ffdc - vector of ffdc data
+ * @return The created PEL's platform log-id
+ */
+uint32_t createPel(std::string i_event,
+ std::map<std::string, std::string> i_additional,
+ std::vector<util::FFDCTuple> i_ffdc)
+{
+ // CreatePELWithFFDCFiles returns log-id and platform log-id
+ std::tuple<uint32_t, uint32_t> pelResp = {0, 0};
+
+ // Sdbus call specifics
+ constexpr auto level = "xyz.openbmc_project.Logging.Entry.Level.Error";
+ constexpr auto service = "xyz.openbmc_project.Logging";
+ constexpr auto path = "/xyz/openbmc_project/logging";
+ constexpr auto interface = "org.open_power.Logging.PEL";
+ constexpr auto function = "CreatePELWithFFDCFiles";
+
+ // Need to provide pid when using create or create-with-ffdc methods
+ i_additional.emplace("_PID", std::to_string(getpid()));
+
+ // Create the PEL
+ try
+ {
+ auto bus = sdbusplus::bus::new_default_system();
+ auto method = bus.new_method_call(service, path, interface, function);
+ method.append(i_event, level, i_additional, i_ffdc);
+
+ auto resp = bus.call(method);
+
+ resp.read(pelResp);
+ }
+ catch (const sdbusplus::exception::SdBusError& e)
+ {
+ std::stringstream ss;
+ ss << "createPel: " << e.what();
+ trace<level::INFO>(ss.str().c_str());
+ }
+
+ // pelResp<0> == log-id, pelResp<1> = platform log-id
+ return std::get<1>(pelResp);
+}
+
+/*
+ * Create a PEL from raw PEL data
+ *
+ * The backend logging code will create a PEL based on the specified PEL data.
+ *
+ * @param i_buffer - buffer containing a raw PEL
+ */
+void createPelRaw(std::vector<uint8_t>& i_buffer)
+{
+ // Sdbus call specifics
+ constexpr auto event = "xyz.open_power.Attn.Error.Terminate";
+ constexpr auto level = "xyz.openbmc_project.Logging.Entry.Level.Error";
+ constexpr auto service = "xyz.openbmc_project.Logging";
+ constexpr auto path = "/xyz/openbmc_project/logging";
+ constexpr auto interface = "xyz.openbmc_project.Logging.Create";
+ constexpr auto function = "Create";
+
+ // Create FFDC file from buffer data
+ util::FFDCFile pelFile{util::FFDCFormat::Text};
+ auto fd = pelFile.getFileDescriptor();
+
+ write(fd, i_buffer.data(), i_buffer.size());
+ lseek(fd, 0, SEEK_SET);
+
+ auto filePath = pelFile.getPath();
+
+ // Additional data for log
+ std::map<std::string, std::string> additional;
+ additional.emplace("RAWPEL", filePath.string());
+ additional.emplace("_PID", std::to_string(getpid()));
+
+ // Create the PEL
+ try
+ {
+ auto bus = sdbusplus::bus::new_default_system();
+ auto method = bus.new_method_call(service, path, interface, function);
+ method.append(event, level, additional);
+ bus.call_noreply(method);
+ }
+ catch (const sdbusplus::exception::SdBusError& e)
+ {
+ std::stringstream ss;
+ ss << "createPelRaw: " << e.what();
+ trace<level::INFO>(ss.str().c_str());
+ }
+}
+
+/**
+ * Create a PEL from an existing PEL
+ *
+ * Create a new PEL based on the specified raw PEL and submit the new PEL
+ * to the backend logging code as a raw PEL. Note that additional data map
+ * here contains data to be committed to the PEL and it can also be used to
+ * create the PEL as it contains needed information.
+ *
+ * @param i_buffer - buffer containing a raw PEL
+ * @param i_additional - additional data to be added to the new PEL
+ */
+void createPelCustom(std::vector<uint8_t>& i_rawPel,
+ std::map<std::string, std::string> i_additional)
+{
+ // create PEL object from buffer
+ auto tiPel = std::make_unique<pel::PelMinimal>(i_rawPel);
+
+ // The additional data contains the TI info as well as the value for the
+ // subystem that provided the TI info. Get the subystem from additional
+ // data and then populate the prmary SRC and SRC words for the custom PEL
+ // based on the sybsystem's TI info.
+ uint8_t subsystem = std::stoi(i_additional["Subsystem"]);
+ tiPel->setSubsystem(subsystem);
+
+ if (static_cast<uint8_t>(pel::SubsystemID::hypervisor) == subsystem)
+ {
+ // populate hypervisor SRC words
+ tiPel->setSrcWords(std::array<uint32_t, pel::numSrcWords>{
+ (uint32_t)std::stoul(i_additional["0x10 SRC Word 12"], 0, 16),
+ (uint32_t)std::stoul(i_additional["0x14 SRC Word 13"], 0, 16),
+ (uint32_t)std::stoul(i_additional["0x18 SRC Word 14"], 0, 16),
+ (uint32_t)std::stoul(i_additional["0x1c SRC Word 15"], 0, 16),
+ (uint32_t)std::stoul(i_additional["0x20 SRC Word 16"], 0, 16),
+ (uint32_t)std::stoul(i_additional["0x24 SRC Word 17"], 0, 16),
+ (uint32_t)std::stoul(i_additional["0x28 SRC Word 18"], 0, 16),
+ (uint32_t)std::stoul(i_additional["0x2c SRC Word 19"], 0, 16)});
+
+ // populate hypervisor primary SRC
+ std::array<char, pel::asciiStringSize> srcChars{'0'};
+ std::string srcString = i_additional["SrcAscii"];
+ srcString.copy(srcChars.data(),
+ std::min(srcString.size(), pel::asciiStringSize), 0);
+ tiPel->setAsciiString(srcChars);
+ }
+ else
+ {
+ // Populate hostboot SRC words - note HB word 0 from the shared info
+ // data (additional data "0x10 HB Word") is reflected in the PEL as
+ // "reason code" so we zero it here. Also note that the first word
+ // in this group of words starts at word 0 and word 1 does not exits.
+ tiPel->setSrcWords(std::array<uint32_t, pel::numSrcWords>{
+ (uint32_t)0x00000000,
+ (uint32_t)std::stoul(i_additional["0x14 HB Word 2"], 0, 16),
+ (uint32_t)std::stoul(i_additional["0x18 HB Word 3"], 0, 16),
+ (uint32_t)std::stoul(i_additional["0x1c HB Word 4"], 0, 16),
+ (uint32_t)std::stoul(i_additional["0x20 HB Word 5"], 0, 16),
+ (uint32_t)std::stoul(i_additional["0x24 HB Word 6"], 0, 16),
+ (uint32_t)std::stoul(i_additional["0x28 HB Word 7"], 0, 16),
+ (uint32_t)std::stoul(i_additional["0x2c HB Word 8"], 0, 16)});
+
+ // populate hostboot primary SRC
+ std::array<char, pel::asciiStringSize> srcChars{'0'};
+ std::string srcString = i_additional["0x30 error_data"];
+ srcString.copy(srcChars.data(),
+ std::min(srcString.size(), pel::asciiStringSize), 0);
+ tiPel->setAsciiString(srcChars);
+ }
+
+ // set severity, event type and action flags
+ tiPel->setSeverity(static_cast<uint8_t>(pel::Severity::termination));
+ tiPel->setType(static_cast<uint8_t>(pel::EventType::na));
+ tiPel->setAction(static_cast<uint16_t>(pel::ActionFlags::service |
+ pel::ActionFlags::report |
+ pel::ActionFlags::call));
+
+ // The raw PEL that we used as the basis for this custom PEL contains the
+ // attention handler trace data and does not needed to be in this PEL so
+ // we remove it here.
+ tiPel->setSectionCount(tiPel->getSectionCount() - 1);
+
+ // Update the raw PEL with the new custom PEL data
+ tiPel->raw(i_rawPel);
+
+ // create PEL from raw data
+ createPelRaw(i_rawPel);
+}
+
+/**
+ * Log an event handled by the attention handler
+ *
+ * Basic (non TI) events will generate a standard message-registry based PEL
+ *
+ * TI events will create two PEL's. One PEL will be informational and will
+ * contain trace information relevent to attention handler. The second PEL
+ * will be specific to the TI type (including the primary SRC) and will be
+ * based off of the TI information provided to the attention handler through
+ * shared TI info data area.
+ *
+ * @param i_event - The event type
+ * @param i_additional - Additional PEL data
+ * @param i_ffdc - FFDC PEL data
+ */
+void event(EventType i_event, std::map<std::string, std::string>& i_additional,
+ const std::vector<util::FFDCFile>& i_ffdc)
{
bool eventValid = false; // assume no event created
+ bool tiEvent = false; // assume not a terminate event
std::string eventName;
@@ -28,15 +428,13 @@
case EventType::Terminate:
eventName = "org.open_power.Attn.Error.Terminate";
eventValid = true;
+ tiEvent = true;
break;
case EventType::Vital:
eventName = "org.open_power.Attn.Error.Vital";
eventValid = true;
break;
case EventType::HwDiagsFail:
- eventName = "org.open_power.HwDiags.Error.Fail";
- eventValid = true;
- break;
case EventType::AttentionFail:
eventName = "org.open_power.Attn.Error.Fail";
eventValid = true;
@@ -48,93 +446,118 @@
if (true == eventValid)
{
- // Get access to logging interface and method for creating log
- auto bus = sdbusplus::bus::new_default_system();
+ // Create PEL with additional data and FFDC data. The newly created
+ // PEL's platform log-id will be returned.
+ auto pelId =
+ createPel(eventName, i_additional, createFFDCTuples(i_ffdc));
- // using direct create method (for additional data)
- auto method = bus.new_method_call(
- "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
- "xyz.openbmc_project.Logging.Create", "CreateWithFFDCFiles");
+ // If this is a TI event we will create an additional PEL that is
+ // specific to the subsystem that generated the TI.
+ if (true == tiEvent)
+ {
+ // get file descriptor and size of information PEL
+ int pelFd = getPelFd(pelId);
- // Create FFDC files containing debug data to store in error log
- std::vector<util::FFDCFile> files{createFFDCFiles()};
+ // if PEL found, read into buffer
+ if (-1 != pelFd)
+ {
+ auto pelSize = lseek(pelFd, 0, SEEK_END);
+ lseek(pelFd, 0, SEEK_SET);
- // Create FFDC tuples used to pass FFDC files to D-Bus method
- std::vector<FFDCTuple> ffdcTuples{createFFDCTuples(files)};
+ // read information PEL into buffer
+ std::vector<uint8_t> buffer(pelSize);
+ read(pelFd, buffer.data(), buffer.size());
+ close(pelFd);
- // attach additional data
- method.append(eventName,
- "xyz.openbmc_project.Logging.Entry.Level.Error",
- i_additional, ffdcTuples);
-
- // log the event
- bus.call_noreply(method);
+ // create PEL from buffer
+ createPelCustom(buffer, i_additional);
+ }
+ }
}
}
-/** @brief commit checkstop event to log */
+/** @brief Commit checkstop event to log */
void eventCheckstop(std::map<std::string, std::string>& i_errors)
{
+ // Additional data for log
std::map<std::string, std::string> additionalData;
- // TODO need multi-error/multi-callout stuff here
-
- // if analyzer isolated errors
- if (!(i_errors.empty()))
- {
- // FIXME TEMP CODE - begin
-
- std::string signature = i_errors.begin()->first;
- std::string chip = i_errors.begin()->second;
-
- additionalData["_PID"] = std::to_string(getpid());
- additionalData["SIGNATURE"] = signature;
- additionalData["CHIP_ID"] = chip;
-
- // FIXME TEMP CODE -end
-
- event(EventType::Checkstop, additionalData);
- }
+ // Create log event with additional data and FFDC data
+ event(EventType::Checkstop, additionalData, createFFDCFiles(nullptr, 0));
}
-/** @brief commit special attention TI event to log */
-void eventTerminate(std::map<std::string, std::string> i_additionalData)
+/**
+ * Commit special attention TI event to log
+ *
+ * Create a event log with provided additional information and standard
+ * FFDC data plus TI FFDC data
+ *
+ * @param i_additional - Additional log data
+ * @param i_ti_InfoData - TI FFDC data
+ */
+void eventTerminate(std::map<std::string, std::string> i_additionalData,
+ char* i_tiInfoData)
{
- event(EventType::Terminate, i_additionalData);
+ // Create log event with aodditional data and FFDC data
+ event(EventType::Terminate, i_additionalData,
+ createFFDCFiles(i_tiInfoData, 0x53));
}
-/** @brief commit SBE vital event to log */
+/** @brief Commit SBE vital event to log */
void eventVital()
{
+ // Additional data for log
std::map<std::string, std::string> additionalData;
- additionalData["_PID"] = std::to_string(getpid());
-
- event(EventType::Vital, additionalData);
+ // Create log event with additional data and FFDC data
+ event(EventType::Vital, additionalData, createFFDCFiles(nullptr, 0));
}
-/** @brief commit analyzer failure event to log */
+/**
+ * Commit analyzer failure event to log
+ *
+ * Create an event log containing the specified error code.
+ *
+ * @param i_error - Error code
+ */
void eventHwDiagsFail(int i_error)
{
+ // Additiona data for log
std::map<std::string, std::string> additionalData;
-
- additionalData["_PID"] = std::to_string(getpid());
-
- event(EventType::HwDiagsFail, additionalData);
-}
-
-/** @brief commit attention handler failure event to log */
-void eventAttentionFail(int i_error)
-{
- std::map<std::string, std::string> additionalData;
-
- additionalData["_PID"] = std::to_string(getpid());
additionalData["ERROR_CODE"] = std::to_string(i_error);
- event(EventType::AttentionFail, additionalData);
+ // Create log event with additional data and FFDC data
+ event(EventType::HwDiagsFail, additionalData, createFFDCFiles(nullptr, 0));
}
-/** @brief parse systemd journal message field */
+/**
+ * Commit attention handler failure event to log
+ *
+ * Create an event log containing the specified error code.
+ *
+ * @param i_error - Error code
+ */
+void eventAttentionFail(int i_error)
+{
+ // Additional data for log
+ std::map<std::string, std::string> additionalData;
+ additionalData["ERROR_CODE"] = std::to_string(i_error);
+
+ // Create log event with additional data and FFDC data
+ event(EventType::AttentionFail, additionalData,
+ createFFDCFiles(nullptr, 0));
+}
+
+/**
+ * Parse systemd journal message field
+ *
+ * Parse the journal looking for the specified field and return the journal
+ * data for that field.
+ *
+ * @param journal - The journal to parse
+ * @param field - Field containing the data to retrieve
+ * @return Data for the speciefied field
+ */
std::string sdjGetFieldValue(sd_journal* journal, const char* field)
{
const char* data{nullptr};
@@ -167,7 +590,17 @@
}
}
-/** @brief get messages from systemd journal */
+/**
+ * Gather messages from the journal
+ *
+ * Fetch journal entry data for all entries with the specified field equal to
+ * the specified value.
+ *
+ * @param field - Field to search on
+ * @param fieldValue - Value to search for
+ * @param max - Maximum number of messages fetch
+ * @return Vector of journal entry data
+ */
std::vector<std::string> sdjGetMessages(const std::string& field,
const std::string& fieldValue,
unsigned int max)
@@ -227,78 +660,4 @@
return messages;
}
-/** @brief create a file containing FFDC data */
-util::FFDCFile createFFDCFile(const std::vector<std::string>& lines)
-{
- // Create FFDC file of type Text
- util::FFDCFile file{util::FFDCFormat::Text};
- int fd = file.getFileDescriptor();
-
- // Write FFDC lines to file
- std::string buffer;
- for (const std::string& line : lines)
- {
- // Copy line to buffer. Add newline if necessary.
- buffer = line;
- if (line.empty() || (line.back() != '\n'))
- {
- buffer += '\n';
- }
-
- // write buffer to file
- write(fd, buffer.c_str(), buffer.size());
- }
-
- // Seek to beginning of file so error logging system can read data
- lseek(fd, 0, SEEK_SET);
-
- return file;
-}
-
-/** @brief Create FDDC files from journal messages of relevant executables */
-std::vector<util::FFDCFile> createFFDCFiles()
-{
- std::vector<util::FFDCFile> files{};
-
- // Executables of interest
- std::vector<std::string> executables{"openpower-hw-diags"};
-
- for (const std::string& executable : executables)
- {
- try
- {
- // get journal messages
- std::vector<std::string> messages =
- sdjGetMessages("SYSLOG_IDENTIFIER", executable, 30);
-
- // Create FFDC file containing the journal messages
- if (!messages.empty())
- {
- files.emplace_back(createFFDCFile(messages));
- }
- }
- catch (const std::exception& e)
- {
- std::stringstream ss;
- ss << "createFFDCFiles: " << e.what();
- trace<level::INFO>(ss.str().c_str());
- }
- }
-
- return files;
-}
-
-/** create tuples of FFDC files */
-std::vector<FFDCTuple> createFFDCTuples(std::vector<util::FFDCFile>& files)
-{
- std::vector<FFDCTuple> ffdcTuples{};
- for (util::FFDCFile& file : files)
- {
- ffdcTuples.emplace_back(
- file.getFormat(), file.getSubType(), file.getVersion(),
- sdbusplus::message::unix_fd(file.getFileDescriptor()));
- }
- return ffdcTuples;
-}
-
} // namespace attn
diff --git a/attn/attn_logging.hpp b/attn/attn_logging.hpp
index 8b5feba..0103a52 100644
--- a/attn/attn_logging.hpp
+++ b/attn/attn_logging.hpp
@@ -29,58 +29,24 @@
/** @brief Maximum length of a single trace event message */
static const size_t trace_msg_max_len = 255;
-/** @brief create trace message */
+/** @brief Create trace message template */
template <level L>
void trace(const char* i_message);
-/** @brief commit checkstop event to log */
+/** @brief Commit checkstop event to log */
void eventCheckstop(std::map<std::string, std::string>& i_errors);
-/** @brief commit special attention TI event to log */
-void eventTerminate(std::map<std::string, std::string> i_additionalData);
+/** @brief Commit special attention TI event to log */
+void eventTerminate(std::map<std::string, std::string> i_additionalData,
+ char* i_tiInfoData);
-/** @brief commit SBE vital event to log */
+/** @brief Commit SBE vital event to log */
void eventVital();
-/** @brief commit analyzer failure event to log */
+/** @brief Commit analyzer failure event to log */
void eventHwDiagsFail(int i_error);
-/** @brief commit attention handler failure event to log */
+/** @brief Commit attention handler failure event to log */
void eventAttentionFail(int i_error);
-using FFDCTuple =
- std::tuple<util::FFDCFormat, uint8_t, uint8_t, sdbusplus::message::unix_fd>;
-
-/**
- * Create an FFDCFile object containing the specified lines of text data.
- *
- * Throws an exception if an error occurs.
- *
- * @param lines lines of text data to write to file
- * @return FFDCFile object
- */
-util::FFDCFile createFFDCFile(const std::vector<std::string>& lines);
-
-/**
- * Create FFDCFile objects containing debug data to store in the error log.
- *
- * If an error occurs, the error is written to the journal but an exception
- * is not thrown.
- *
- * @param journal system journal
- * @return vector of FFDCFile objects
- */
-std::vector<util::FFDCFile> createFFDCFiles();
-
-/**
- * Create FFDCTuple objects corresponding to the specified FFDC files.
- *
- * The D-Bus method to create an error log requires a vector of tuples to
- * pass in the FFDC file information.
- *
- * @param files FFDC files
- * @return vector of FFDCTuple objects
- */
-std::vector<FFDCTuple> createFFDCTuples(std::vector<util::FFDCFile>& files);
-
} // namespace attn
diff --git a/attn/meson.build b/attn/meson.build
index 815e038..d42289d 100644
--- a/attn/meson.build
+++ b/attn/meson.build
@@ -37,6 +37,14 @@
'vital_handler.cpp',
)
+# for custom/raw PEL creation
+pel_src = files(
+ 'pel/pel_minimal.cpp',
+ 'pel/private_header.cpp',
+ 'pel/primary_src.cpp',
+ 'pel/user_header.cpp',
+)
+
# Library dependencies.
attn_deps = [
libgpiod,
@@ -48,6 +56,7 @@
attn_lib = static_library(
'attn_lib',
attn_src,
+ pel_src,
include_directories : incdir,
dependencies : attn_deps,
cpp_args : [boost_args, test_arg],
diff --git a/attn/pel/pel_common.hpp b/attn/pel/pel_common.hpp
new file mode 100644
index 0000000..d0f3e15
--- /dev/null
+++ b/attn/pel/pel_common.hpp
@@ -0,0 +1,69 @@
+#pragma once
+
+#include <cstddef> // for size_t
+
+namespace attn
+{
+namespace pel
+{
+
+enum class SectionID
+{
+ privateHeader = 0x5048, // 'PH'
+ userHeader = 0x5548, // 'UH'
+ primarySRC = 0x5053, // 'PS'
+};
+
+enum class ComponentID
+{
+ attentionHandler = 0xd100
+};
+
+enum class CreatorID
+{
+ hostboot = 'B',
+ hypervisor = 'H',
+ openbmc = 'O'
+};
+
+enum class SubsystemID
+{
+ hypervisor = 0x82,
+ hostboot = 0x8a,
+ openbmc = 0x8d
+};
+
+enum class Severity
+{
+ information = 0x00,
+ termination = 0x51
+};
+
+enum class EventType
+{
+ na = 0x00,
+ trace = 0x02
+};
+
+enum class ActionFlags
+{
+ service = 0x8000,
+ report = 0x2000,
+ call = 0x0800
+};
+
+inline ActionFlags operator|(ActionFlags a, ActionFlags b)
+{
+ return static_cast<ActionFlags>(static_cast<int>(a) | static_cast<int>(b));
+}
+
+enum class EventScope
+{
+ platform = 0x03
+};
+
+constexpr size_t numSrcWords = 8; // number of SRC hex words
+const size_t asciiStringSize = 32; // size of SRC ascii string
+
+} // namespace pel
+} // namespace attn
diff --git a/attn/pel/pel_minimal.cpp b/attn/pel/pel_minimal.cpp
new file mode 100644
index 0000000..9d7dfc1
--- /dev/null
+++ b/attn/pel/pel_minimal.cpp
@@ -0,0 +1,95 @@
+#include "pel_minimal.hpp"
+
+#include "stream.hpp"
+
+namespace attn
+{
+namespace pel
+{
+
+PelMinimal::PelMinimal(std::vector<uint8_t>& data)
+{
+ Stream pelData{data};
+
+ _ph = std::make_unique<PrivateHeader>(pelData);
+ _uh = std::make_unique<UserHeader>(pelData);
+ _ps = std::make_unique<PrimarySrc>(pelData);
+}
+
+void PelMinimal::raw(std::vector<uint8_t>& pelBuffer) const
+{
+ Stream pelData{pelBuffer};
+
+ // stream from object to buffer
+ _ph->flatten(pelData);
+ _uh->flatten(pelData);
+ _ps->flatten(pelData);
+}
+
+size_t PelMinimal::size() const
+{
+ size_t size = 0;
+
+ // size of private header section
+ if (_ph)
+ {
+ size += _ph->header().size;
+ }
+
+ // size of user header section
+ if (_uh)
+ {
+ size += _uh->header().size;
+ }
+
+ // size of primary SRC section
+ if (_ps)
+ {
+ size += _ph->header().size;
+ }
+
+ return ((size > _maxPELSize) ? _maxPELSize : size);
+}
+
+void PelMinimal::setSubsystem(uint8_t subsystem)
+{
+ _uh->setSubsystem(subsystem);
+}
+
+void PelMinimal::setSeverity(uint8_t severity)
+{
+ _uh->setSeverity(severity);
+}
+
+void PelMinimal::setType(uint8_t type)
+{
+ _uh->setType(type);
+}
+
+void PelMinimal::setAction(uint16_t action)
+{
+ _uh->setAction(action);
+}
+
+void PelMinimal::setSrcWords(std::array<uint32_t, numSrcWords> srcWords)
+{
+ _ps->setSrcWords(srcWords);
+}
+
+void PelMinimal::setAsciiString(std::array<char, asciiStringSize> asciiString)
+{
+ _ps->setAsciiString(asciiString);
+}
+
+uint8_t PelMinimal::getSectionCount()
+{
+ return _ph->getSectionCount();
+}
+
+void PelMinimal::setSectionCount(uint8_t sectionCount)
+{
+ _ph->setSectionCount(sectionCount);
+}
+
+} // namespace pel
+} // namespace attn
diff --git a/attn/pel/pel_minimal.hpp b/attn/pel/pel_minimal.hpp
new file mode 100644
index 0000000..cde0ead
--- /dev/null
+++ b/attn/pel/pel_minimal.hpp
@@ -0,0 +1,144 @@
+#pragma once
+
+#include "pel_common.hpp"
+#include "primary_src.hpp"
+#include "private_header.hpp"
+#include "user_header.hpp"
+
+#include <vector>
+
+namespace attn
+{
+namespace pel
+{
+
+/** @class PelMinimal
+ *
+ * @brief Class for a minimal platform event log (PEL)
+ *
+ * This class can be used to create form a PEL and create a raw PEL file. The
+ * implementation based on "Platform Event Log and SRC PLDD v1.1"
+ *
+ * This PEL consists of the following position dependent sections:
+ *
+ * |----------+------------------------------|
+ * | length | section |
+ * |----------+------------------------------|
+ * | 48 | Private Header Section |
+ * |----------+------------------------------|
+ * | 24 | User Header Section |
+ * |----------+------------------------------|
+ * | 72 | Primary SRC Section |
+ * |----------+------------------------------|
+ */
+class PelMinimal
+{
+ public:
+ PelMinimal() = delete;
+ ~PelMinimal() = default;
+ PelMinimal(const PelMinimal&) = delete;
+ PelMinimal& operator=(const PelMinimal&) = delete;
+ PelMinimal(PelMinimal&&) = delete;
+ PelMinimal& operator=(PelMinimal&&) = delete;
+
+ /**
+ * @brief Create a minimal PEL object from raw data
+ *
+ * @param[in] pelBuffer - buffer containing a raw PEL
+ */
+ PelMinimal(std::vector<uint8_t>& data);
+
+ /**
+ * @brief Stream raw PEL data to buffer
+ *
+ * @param[out] pelBuffer - What the data will be written to
+ */
+ void raw(std::vector<uint8_t>& pelBuffer) const;
+
+ /**
+ * @brief Set the User Header subsystem field
+ *
+ * @param[in] subsystem - The subsystem value
+ */
+ void setSubsystem(uint8_t subsystem);
+
+ /**
+ * @brief Set the User Header severity field
+ *
+ * @param[in] severity - The severity to set
+ */
+ void setSeverity(uint8_t severity);
+
+ /**
+ * @brief Set the User Header event type field
+ *
+ * @param[in] type - The event type
+ */
+ void setType(uint8_t type);
+
+ /**
+ * @brief Set the User Header action flags field
+ *
+ * @param[in] action - The action flags to set
+ */
+ void setAction(uint16_t action);
+
+ /**
+ * @brief Set the Primary SRC section SRC words
+ *
+ * @param[in] srcWords - The SRC words
+ */
+ void setSrcWords(std::array<uint32_t, numSrcWords> srcWords);
+
+ /**
+ * @brief Set the Primary SRC section ascii string field
+ *
+ * @param[in] asciiString - The ascii string
+ */
+ void setAsciiString(std::array<char, asciiStringSize> asciiString);
+
+ /**
+ * @brief Get section count from the private header
+ *
+ * @return Number of sections
+ */
+ uint8_t getSectionCount();
+
+ /**
+ * @brief Set section count in private heasder
+ *
+ * @param[in] sectionCount - Number of sections
+ */
+ void setSectionCount(uint8_t sectionCount);
+
+ private:
+ /**
+ * @brief Maximum PEL size
+ */
+ static constexpr size_t _maxPELSize = 16384;
+
+ /**
+ * @brief Returns the size of the PEL
+ *
+ * @return size_t The PEL size in bytes
+ */
+ size_t size() const;
+
+ /**
+ * @brief PEL Private Header
+ */
+ std::unique_ptr<PrivateHeader> _ph;
+
+ /**
+ * @brief PEL User Header
+ */
+ std::unique_ptr<UserHeader> _uh;
+
+ /**
+ * @brief PEL Primary SRC
+ */
+ std::unique_ptr<PrimarySrc> _ps;
+};
+
+} // namespace pel
+} // namespace attn
diff --git a/attn/pel/pel_section.hpp b/attn/pel/pel_section.hpp
new file mode 100644
index 0000000..ca8dce4
--- /dev/null
+++ b/attn/pel/pel_section.hpp
@@ -0,0 +1,59 @@
+#pragma once
+
+#include "section_header.hpp"
+
+namespace attn
+{
+namespace pel
+{
+/**
+ * @class Section
+ *
+ * The base class for a PEL section. It contains the SectionHeader
+ * as all sections start with it.
+ *
+ */
+class Section
+{
+ public:
+ Section() = default;
+ virtual ~Section() = default;
+ Section(const Section&) = default;
+ Section& operator=(const Section&) = default;
+ Section(Section&&) = default;
+ Section& operator=(Section&&) = default;
+
+ /**
+ * @brief Returns a reference to the SectionHeader
+ */
+ const SectionHeader& header() const
+ {
+ return _header;
+ }
+
+ /**
+ * @brief Flatten the section into the stream
+ *
+ * @param[in] stream - The stream to write to
+ */
+ virtual void flatten(Stream& stream) const = 0;
+
+ protected:
+ /**
+ * @brief Returns the flattened size of the section header
+ */
+ static constexpr size_t flattenedSize()
+ {
+ return SectionHeader::flattenedSize();
+ }
+
+ /**
+ * @brief The section header structure.
+ *
+ * Filled in by derived classes.
+ */
+ SectionHeader _header;
+};
+
+} // namespace pel
+} // namespace attn
diff --git a/attn/pel/primary_src.cpp b/attn/pel/primary_src.cpp
new file mode 100644
index 0000000..0123395
--- /dev/null
+++ b/attn/pel/primary_src.cpp
@@ -0,0 +1,50 @@
+#include "primary_src.hpp"
+
+namespace attn
+{
+namespace pel
+{
+
+PrimarySrc::PrimarySrc(Stream& pel)
+{
+ unflatten(pel);
+}
+
+void PrimarySrc::flatten(Stream& stream) const
+{
+ stream << _header << _version << _flags << _reserved1B << _wordCount
+ << _reserved2B << _size;
+
+ for (auto& word : _srcWords)
+ {
+ stream << word;
+ }
+
+ stream.write(_asciiString.data(), _asciiString.size());
+}
+
+void PrimarySrc::unflatten(Stream& stream)
+{
+ stream >> _header >> _version >> _flags >> _reserved1B >> _wordCount >>
+ _reserved2B >> _size;
+
+ for (auto& word : _srcWords)
+ {
+ stream >> word;
+ }
+
+ stream.read(_asciiString.data(), _asciiString.size());
+}
+
+void PrimarySrc::setSrcWords(std::array<uint32_t, numSrcWords> srcWords)
+{
+ _srcWords = srcWords;
+}
+
+void PrimarySrc::setAsciiString(std::array<char, asciiStringSize> asciiString)
+{
+ _asciiString = asciiString;
+}
+
+} // namespace pel
+} // namespace attn
diff --git a/attn/pel/primary_src.hpp b/attn/pel/primary_src.hpp
new file mode 100644
index 0000000..659cb48
--- /dev/null
+++ b/attn/pel/primary_src.hpp
@@ -0,0 +1,150 @@
+#pragma once
+
+#include "pel_common.hpp"
+#include "pel_section.hpp"
+#include "stream.hpp"
+
+namespace attn
+{
+namespace pel
+{
+
+/**
+ * @class PrimarySrc
+ *
+ * @brief This class represents the primary SRC sections in the PEL.
+ *
+ * |--------+--------------------------------------------|
+ * | length | field |
+ * |--------+--------------------------------------------|
+ * | 1 | Version = 0x02 |
+ * |--------+--------------------------------------------|
+ * | 1 | Flags = 0x00 (no additional data sections) |
+ * |--------+--------------------------------------------|
+ * | 1 | reserved |
+ * |--------+--------------------------------------------|
+ * | 1 | Number of words of hex data + 1 = 0x09 |
+ * |--------+--------------------------------------------|
+ * | 2 | reserved |
+ * |--------+--------------------------------------------|
+ * | 2 | Total length of SRC in bytes = 0x48 |
+ * |--------+--------------------------------------------|
+ * | 4 | Hex Word 2 (word 1 intentionally skipped) |
+ * |--------+--------------------------------------------|
+ * | 4 | Hex Word 3 |
+ * |--------+--------------------------------------------|
+ * | 4 | Hex Word 4 |
+ * |--------+--------------------------------------------|
+ * | 4 | Hex Word 5 |
+ * |--------+--------------------------------------------|
+ * | 4 | Hex Word 6 |
+ * |--------+--------------------------------------------|
+ * | 4 | Hex Word 7 |
+ * |--------+--------------------------------------------|
+ * | 4 | Hex Word 8 |
+ * |--------+--------------------------------------------|
+ * | 4 | Hex Word 9 |
+ * |--------+--------------------------------------------|
+ * | 32 | ASCII String |
+ * |--------+--------------------------------------------|
+ */
+class PrimarySrc : public Section
+{
+ public:
+ enum HeaderFlags
+ {
+ additionalSections = 0x01,
+ powerFaultEvent = 0x02,
+ hypDumpInit = 0x04,
+ i5OSServiceEventBit = 0x10,
+ virtualProgressSRC = 0x80
+ };
+
+ PrimarySrc() = delete;
+ ~PrimarySrc() = default;
+ PrimarySrc(const PrimarySrc&) = delete;
+ PrimarySrc& operator=(const PrimarySrc&) = delete;
+ PrimarySrc(PrimarySrc&&) = delete;
+ PrimarySrc& operator=(PrimarySrc&&) = delete;
+
+ /**
+ * @brief Constructor
+ *
+ * Fills in this class's data fields from raw data.
+ *
+ * @param[in] pel - the PEL data stream
+ */
+ explicit PrimarySrc(Stream& pel);
+
+ /**
+ * @brief Flatten the section into the stream
+ *
+ * @param[in] stream - The stream to write to
+ */
+ void flatten(Stream& stream) const override;
+
+ /**
+ * @brief Fills in the object from the stream data
+ *
+ * @param[in] stream - The stream to read from
+ */
+ void unflatten(Stream& stream);
+
+ /**
+ * @brief Set the SRC words
+ *
+ * @param[in] srcWords - The SRC words
+ */
+ void setSrcWords(std::array<uint32_t, numSrcWords> srcWords);
+
+ /**
+ * @brief Set the ascii string field
+ *
+ * @param[in] asciiString - The ascii string
+ */
+ void setAsciiString(std::array<char, asciiStringSize> asciiString);
+
+ private:
+ /**
+ * @brief The SRC version field
+ */
+ uint8_t _version = 0x02;
+
+ /**
+ * @brief The SRC flags field
+ */
+ uint8_t _flags = 0;
+
+ /**
+ * @brief A byte of reserved data after the flags field
+ */
+ uint8_t _reserved1B = 0;
+
+ /**
+ * @brief The hex data word count.
+ */
+ uint8_t _wordCount = numSrcWords + 1; // +1 for backward compatability
+
+ /**
+ * @brief Two bytes of reserved data after the hex word count
+ */
+ uint16_t _reserved2B = 0;
+
+ /**
+ * @brief The total size of the SRC section (w/o section header)
+ */
+ uint16_t _size = 72; // 72 (bytes) = size of basic SRC section
+
+ /**
+ * @brief The SRC 'hex words'.
+ */
+ std::array<uint32_t, numSrcWords> _srcWords;
+
+ /**
+ * @brief The 32 byte ASCII character string of the SRC
+ */
+ std::array<char, asciiStringSize> _asciiString;
+};
+
+} // namespace pel
+} // namespace attn
diff --git a/attn/pel/private_header.cpp b/attn/pel/private_header.cpp
new file mode 100644
index 0000000..18d06d4
--- /dev/null
+++ b/attn/pel/private_header.cpp
@@ -0,0 +1,58 @@
+#include "private_header.hpp"
+
+namespace attn
+{
+namespace pel
+{
+
+PrivateHeader::PrivateHeader(Stream& pel)
+{
+ unflatten(pel);
+}
+
+void PrivateHeader::flatten(Stream& stream) const
+{
+ stream << _header << _createTimestamp << _commitTimestamp << _creatorID
+ << _reservedByte1 << _reservedByte2 << _sectionCount << _obmcLogID
+ << _creatorVersion << _plid << _id;
+}
+
+void PrivateHeader::unflatten(Stream& stream)
+{
+ stream >> _header >> _createTimestamp >> _commitTimestamp >> _creatorID >>
+ _reservedByte1 >> _reservedByte2 >> _sectionCount >> _obmcLogID >>
+ _creatorVersion >> _plid >> _id;
+}
+
+uint8_t PrivateHeader::getSectionCount()
+{
+ return _sectionCount;
+}
+
+void PrivateHeader::setSectionCount(uint8_t sectionCount)
+{
+ _sectionCount = sectionCount;
+}
+
+/*
+Stream& operator<<(Stream& s, const CreatorVersion& cv)
+{
+ for (size_t i = 0; i < sizeof(CreatorVersion); i++)
+ {
+ s << cv.version[i];
+ }
+ return s;
+}
+
+Stream& operator>>(Stream& s, CreatorVersion& cv)
+{
+ for (size_t i = 0; i < sizeof(CreatorVersion); i++)
+ {
+ s >> cv.version[i];
+ }
+ return s;
+}
+*/
+
+} // namespace pel
+} // namespace attn
diff --git a/attn/pel/private_header.hpp b/attn/pel/private_header.hpp
new file mode 100644
index 0000000..3b80968
--- /dev/null
+++ b/attn/pel/private_header.hpp
@@ -0,0 +1,191 @@
+#pragma once
+
+//#include "bcd_time.hpp"
+#include "pel_common.hpp"
+#include "pel_section.hpp"
+#include "stream.hpp"
+
+namespace attn
+{
+namespace pel
+{
+
+// creator version field type, init to null terminated
+// struct CreatorVersion
+//{
+// uint8_t version[8];
+
+// CreatorVersion()
+// {
+// memset(version, '\0', sizeof(version));
+// }
+//};
+
+/**
+ * @class PrivateHeader
+ *
+ * This represents the Private Header section in a PEL. It is required,
+ * and it is always the first section.
+ *
+ * The Section base class handles the SectionHeader structure that every
+ * PEL section has at offset zero.
+ *
+ * |--------+----------------------+----------+----------+---------------|
+ * | length | byte0 | byte1 | byte2 | byte3 |
+ * |--------+----------------------+----------+----------+---------------|
+ * | 8 | Section Header |
+ * | | |
+ * |--------+------------------------------------------------------------|
+ * | 8 | Timestamp - Creation |
+ * | | |
+ * |--------+------------------------------------------------------------|
+ * | 8 | Timestamp - Commit |
+ * | | |
+ * |--------+----------------------+----------+----------+---------------|
+ * | 4 | Creator ID | reserved | reserved | section count |
+ * |--------+----------------------+----------+----------+---------------|
+ * | 4 | OpenBMC Event Log ID |
+ * |--------+------------------------------------------------------------|
+ * | 8 | Creator Implementation |
+ * | | |
+ * |--------+------------------------------------------------------------|
+ * | 4 | Platform Log ID |
+ * |--------+------------------------------------------------------------|
+ * | 4 | Log Entry ID |
+ * |--------+------------------------------------------------------------|
+ */
+class PrivateHeader : public Section
+{
+ public:
+ PrivateHeader() = delete;
+ ~PrivateHeader() = default;
+ PrivateHeader(const PrivateHeader&) = default;
+ PrivateHeader& operator=(const PrivateHeader&) = default;
+ PrivateHeader(PrivateHeader&&) = default;
+ PrivateHeader& operator=(PrivateHeader&&) = default;
+
+ /**
+ * @brief Constructor
+ *
+ * Fills in this class's data fields from raw data.
+ *
+ * @param[in] pel - the PEL data stream
+ *
+ */
+ explicit PrivateHeader(Stream& pel);
+
+ /**
+ * @brief Flatten the section into the stream
+ *
+ * @param[in] stream - The stream to write to
+ */
+ void flatten(Stream& stream) const override;
+
+ /**
+ * @brief Fills in the object from the stream data
+ *
+ * @param[in] stream - The stream to read from
+ */
+ void unflatten(Stream& stream);
+
+ /**
+ * @brief Returns the size of this section when flattened into a PEL
+ *
+ * @return size_t - the size of the section
+ */
+ static constexpr size_t flattenedSize()
+ {
+ return Section::flattenedSize() + sizeof(_createTimestamp) +
+ sizeof(_commitTimestamp) + sizeof(_creatorID) +
+ sizeof(_reservedByte1) + sizeof(_reservedByte2) +
+ sizeof(_sectionCount) + sizeof(_obmcLogID) +
+ sizeof(_creatorVersion) + sizeof(_plid) + sizeof(_id);
+ }
+
+ /**
+ * @brief Get the total number of sections in this PEL
+ *
+ * @return Number of sections
+ */
+ uint8_t getSectionCount();
+
+ /**
+ * @brief Set the total number of sections in this PEL
+ *
+ * @param[in] sectionCount - Number of sections
+ */
+ void setSectionCount(uint8_t sectionCount);
+
+ private:
+ /**
+ * @brief The creation time timestamp
+ */
+ uint64_t _createTimestamp;
+ // BCDTime _createTimestamp;
+
+ /**
+ * @brief The commit time timestamp
+ */
+ uint64_t _commitTimestamp;
+ // BCDTime _commitTimestamp;
+
+ /**
+ * @brief The creator ID field
+ */
+ uint8_t _creatorID;
+
+ /**
+ * @brief A reserved byte.
+ */
+ uint8_t _reservedByte1 = 0;
+
+ /**
+ * @brief A reserved byte.
+ */
+ uint8_t _reservedByte2 = 0;
+
+ /**
+ * @brief Total number of sections in the PEL
+ */
+ uint8_t _sectionCount = 3; // private header, user header, primary src = 3
+
+ /**
+ * @brief The OpenBMC event log ID that corresponds to this PEL.
+ */
+ uint32_t _obmcLogID = 0;
+
+ /**
+ * @brief The creator subsystem version field
+ */
+ uint64_t _creatorVersion;
+ // CreatorVersion _creatorVersion;
+
+ /**
+ * @brief The platform log ID field
+ */
+ uint32_t _plid;
+
+ /**
+ * @brief The log entry ID field
+ */
+ uint32_t _id = 0;
+};
+
+/**
+ * @brief Stream insertion operator for the CreatorVersion
+ *
+ * @param[out] s - the stream
+ * @param[in] cv - the CreatorVersion object
+ */
+// Stream& operator<<(Stream& s, const CreatorVersion& cv);
+
+/**
+ * @brief Stream extraction operator for the CreatorVersion
+ *
+ * @param[in] s - the stream
+ * @param[out] cv - the CreatorVersion object
+ */
+// Stream& operator>>(Stream& s, CreatorVersion& cv);
+
+} // namespace pel
+} // namespace attn
diff --git a/attn/pel/section_header.hpp b/attn/pel/section_header.hpp
new file mode 100644
index 0000000..ae0a2c5
--- /dev/null
+++ b/attn/pel/section_header.hpp
@@ -0,0 +1,130 @@
+#pragma once
+
+#include "stream.hpp"
+
+#include <cstdint>
+
+namespace attn
+{
+namespace pel
+{
+
+/**
+ * @class SectionHeader
+ *
+ * @brief This 8-byte header is at the start of every PEL section.
+ *
+ * |--------+------------+------------+-----------+------------|
+ * | length | byte0 | byte1 | byte2 | byte3 |
+ * |--------+------------+------------+-----------+------------|
+ * | 4 | Section ID | Section Length |
+ * |--------+------------+-------------------------------------|
+ * | 4 | Version | Sub-type | Component ID |
+ * |--------+--------------------------------------------------|
+ *
+ * Section ID:
+ * A two-ASCII character field which uniquely identifies the type of section.
+ *
+ * Section length:
+ * Length in bytes of the section, including the entire section header.
+ *
+ * Section Version:
+ * A one byte integer intended to identify differences in header structures.
+ *
+ * Section sub-type:
+ * Optional. Additional identifier describing the section.
+ *
+ * Component IDs:
+ * Optional. By convention the creator's component ID is placed in the
+ * component ID field of the Private Header and the committing component
+ * ID is placed in the component ID field of the User Header.
+ */
+struct SectionHeader
+{
+ public:
+ /**
+ * @brief Constructor
+ */
+ SectionHeader() : id(0), size(0), version(0), subType(0), componentID(0) {}
+
+ /**
+ * @brief Constructor
+ *
+ * @param[in] id - the ID field
+ * @param[in] size - the size field
+ * @param[in] version - the version field
+ * @param[in] subType - the sub-type field
+ * @param[in] componentID - the component ID field
+ */
+ SectionHeader(uint16_t id, uint16_t size, uint8_t version, uint8_t subType,
+ uint16_t componentID) :
+ id(id),
+ size(size), version(version), subType(subType), componentID(componentID)
+ {}
+
+ /**
+ * @brief A two character ASCII field which identifies the section type.
+ */
+ uint16_t id;
+
+ /**
+ * @brief The size of the section in bytes, including this section header.
+ */
+ uint16_t size;
+
+ /**
+ * @brief The section format version.
+ */
+ uint8_t version;
+
+ /**
+ * @brief The section sub-type.
+ */
+ uint8_t subType;
+
+ /**
+ * @brief The component ID, which has various meanings depending on the
+ * section.
+ */
+ uint16_t componentID;
+
+ /**
+ * @brief Returns the size of header when flattened into a PEL.
+ *
+ * @return size_t - the size of the header
+ */
+ static constexpr size_t flattenedSize()
+ {
+ return sizeof(id) + sizeof(size) + sizeof(version) + sizeof(subType) +
+ sizeof(componentID);
+ }
+};
+
+/**
+ * @brief Stream extraction operator for the SectionHeader
+ *
+ * @param[in] s - the stream
+ * @param[out] header - the SectionHeader object
+ */
+inline Stream& operator>>(Stream& s, SectionHeader& header)
+{
+ s >> header.id >> header.size >> header.version >> header.subType >>
+ header.componentID;
+ return s;
+}
+
+/**
+ * @brief Stream insertion operator for the section header
+ *
+ * @param[out] s - the stream
+ * @param[in] header - the SectionHeader object
+ */
+inline Stream& operator<<(Stream& s, const SectionHeader& header)
+{
+ s << header.id << header.size << header.version << header.subType
+ << header.componentID;
+ return s;
+}
+
+} // namespace pel
+} // namespace attn
diff --git a/attn/pel/stream.hpp b/attn/pel/stream.hpp
new file mode 100644
index 0000000..a82bd8a
--- /dev/null
+++ b/attn/pel/stream.hpp
@@ -0,0 +1,372 @@
+#pragma once
+
+#include <arpa/inet.h>
+#include <byteswap.h>
+
+#include <cassert>
+#include <cstring>
+#include <memory>
+#include <stdexcept>
+#include <string>
+#include <vector>
+
+namespace attn
+{
+namespace pel
+{
+
+namespace detail
+{
+/**
+ * @brief A host-to-network implementation for uint64_t
+ *
+ * @param[in] value - the value to convert to
+ * @return uint64_t - the byteswapped value
+ */
+inline uint64_t htonll(uint64_t value)
+{
+ return bswap_64(value);
+}
+
+/**
+ * @brief A network-to-host implementation for uint64_t
+ *
+ * @param[in] value - the value to convert to
+ * @return uint64_t - the byteswapped value
+ */
+inline uint64_t ntohll(uint64_t value)
+{
+ return bswap_64(value);
+}
+} // namespace detail
+
+/**
+ * @class Stream
+ *
+ * This class is used for getting data types into and out of a vector<uint8_t>
+ * that contains data in network byte (big endian) ordering.
+ */
+class Stream
+{
+ public:
+ Stream() = delete;
+ ~Stream() = default;
+ Stream(const Stream&) = default;
+ Stream& operator=(const Stream&) = default;
+ Stream(Stream&&) = default;
+ Stream& operator=(Stream&&) = default;
+
+ /**
+ * @brief Constructor
+ *
+ * @param[in] data - the vector of data
+ */
+ explicit Stream(std::vector<uint8_t>& data) : _data(data), _offset(0) {}
+
+ /**
+ * @brief Constructor
+ *
+ * @param[in] data - the vector of data
+ * @param[in] offset - the starting offset
+ */
+ Stream(std::vector<uint8_t>& data, std::size_t offset) :
+ _data(data), _offset(offset)
+ {
+ if (_offset >= _data.size())
+ {
+ throw std::out_of_range("Offset out of range");
+ }
+ }
+
+ /**
+ * @brief Extraction operator for a uint8_t
+ *
+ * @param[out] value - filled in with the value
+ * @return Stream&
+ */
+ Stream& operator>>(uint8_t& value)
+ {
+ read(&value, 1);
+ return *this;
+ }
+
+ /**
+ * @brief Extraction operator for a char
+ *
+ * @param[out] value -filled in with the value
+ * @return Stream&
+ */
+ Stream& operator>>(char& value)
+ {
+ read(&value, 1);
+ return *this;
+ }
+
+ /**
+ * @brief Extraction operator for a uint16_t
+ *
+ * @param[out] value -filled in with the value
+ * @return Stream&
+ */
+ Stream& operator>>(uint16_t& value)
+ {
+ read(&value, 2);
+ value = htons(value);
+ return *this;
+ }
+
+ /**
+ * @brief Extraction operator for a uint32_t
+ *
+ * @param[out] value -filled in with the value
+ * @return Stream&
+ */
+ Stream& operator>>(uint32_t& value)
+ {
+ read(&value, 4);
+ value = htonl(value);
+ return *this;
+ }
+
+ /**
+ * @brief Extraction operator for a uint64_t
+ *
+ * @param[out] value -filled in with the value
+ * @return Stream&
+ */
+ Stream& operator>>(uint64_t& value)
+ {
+ read(&value, 8);
+ value = detail::htonll(value);
+ return *this;
+ }
+
+ /**
+ * @brief Extraction operator for a std::vector<uint8_t>
+ *
+ * The vector's size is the amount extracted.
+ *
+ * @param[out] value - filled in with the value
+ * @return Stream&
+ */
+ Stream& operator>>(std::vector<uint8_t>& value)
+ {
+ if (!value.empty())
+ {
+ read(value.data(), value.size());
+ }
+ return *this;
+ }
+
+ /**
+ * @brief Extraction operator for a std::vector<char>
+ *
+ * The vector's size is the amount extracted.
+ *
+ * @param[out] value - filled in with the value
+ * @return Stream&
+ */
+ Stream& operator>>(std::vector<char>& value)
+ {
+ if (!value.empty())
+ {
+ read(value.data(), value.size());
+ }
+ return *this;
+ }
+
+ /**
+ * @brief Insert operator for a uint8_t
+ *
+ * @param[in] value - the value to write to the stream
+ * @return Stream&
+ */
+ Stream& operator<<(uint8_t value)
+ {
+ write(&value, 1);
+ return *this;
+ }
+
+ /**
+ * @brief Insert operator for a char
+ *
+ * @param[in] value - the value to write to the stream
+ * @return Stream&
+ */
+ Stream& operator<<(char value)
+ {
+ write(&value, 1);
+ return *this;
+ }
+
+ /**
+ * @brief Insert operator for a uint16_t
+ *
+ * @param[in] value - the value to write to the stream
+ * @return Stream&
+ */
+ Stream& operator<<(uint16_t value)
+ {
+ uint16_t data = ntohs(value);
+ write(&data, 2);
+ return *this;
+ }
+
+ /**
+ * @brief Insert operator for a uint32_t
+ *
+ * @param[in] value - the value to write to the stream
+ * @return Stream&
+ */
+ Stream& operator<<(uint32_t value)
+ {
+ uint32_t data = ntohl(value);
+ write(&data, 4);
+ return *this;
+ }
+
+ /**
+ * @brief Insert operator for a uint64_t
+ *
+ * @param[in] value - the value to write to the stream
+ * @return Stream&
+ */
+ Stream& operator<<(uint64_t value)
+ {
+ uint64_t data = detail::ntohll(value);
+ write(&data, 8);
+ return *this;
+ }
+
+ /**
+ * @brief Insert operator for a std::vector<uint8_t>
+ *
+ * The full vector is written to the stream.
+ *
+ * @param[in] value - the value to write to the stream
+ * @return Stream&
+ */
+ Stream& operator<<(const std::vector<uint8_t>& value)
+ {
+ if (!value.empty())
+ {
+ write(value.data(), value.size());
+ }
+ return *this;
+ }
+
+ /**
+ * @brief Insert operator for a std::vector<char>
+ *
+ * The full vector is written to the stream.
+ *
+ * @param[in] value - the value to write to the stream
+ * @return Stream&
+ */
+ Stream& operator<<(const std::vector<char>& value)
+ {
+ if (!value.empty())
+ {
+ write(value.data(), value.size());
+ }
+ return *this;
+ }
+
+ /**
+ * @brief Sets the offset of the stream
+ *
+ * @param[in] newOffset - the new offset
+ */
+ void offset(std::size_t newOffset)
+ {
+ if (newOffset >= _data.size())
+ {
+ throw std::out_of_range("new offset out of range");
+ }
+
+ _offset = newOffset;
+ }
+
+ /**
+ * @brief Returns the current offset of the stream
+ *
+ * @return size_t - the offset
+ */
+ std::size_t offset() const
+ {
+ return _offset;
+ }
+
+ /**
+ * @brief Returns the remaining bytes left between the current offset
+ * and the data size.
+ *
+ * @return size_t - the remaining size
+ */
+ std::size_t remaining() const
+ {
+ assert(_data.size() >= _offset);
+ return _data.size() - _offset;
+ }
+
+ /**
+ * @brief Reads a specified number of bytes out of a stream
+ *
+ * @param[out] out - filled in with the data
+ * @param[in] size - the size to read
+ */
+ void read(void* out, std::size_t size)
+ {
+ rangeCheck(size);
+ memcpy(out, &_data[_offset], size);
+ _offset += size;
+ }
+
+ /**
+ * @brief Writes a specified number of bytes into the stream
+ *
+ * @param[in] in - the data to write
+ * @param[in] size - the size to write
+ */
+ void write(const void* in, std::size_t size)
+ {
+ size_t newSize = _offset + size;
+ if (newSize > _data.size())
+ {
+ _data.resize(newSize, 0);
+ }
+ memcpy(&_data[_offset], in, size);
+ _offset += size;
+ }
+
+ private:
+ /**
+ * @brief Throws an exception if the size passed in plus the current
+ * offset is bigger than the current data size.
+ * @param[in] size - the size to check
+ */
+ void rangeCheck(std::size_t size)
+ {
+ if (_offset + size > _data.size())
+ {
+ std::string msg{"Attempted stream overflow: offset "};
+ msg += std::to_string(_offset) + " buffer size " +
+ std::to_string(_data.size()) + " op size " +
+ std::to_string(size);
+ throw std::out_of_range(msg.c_str());
+ }
+ }
+
+ /**
+ * @brief The data that the stream accesses.
+ */
+ std::vector<uint8_t>& _data;
+
+ /**
+ * @brief The current offset of the stream.
+ */
+ std::size_t _offset;
+};
+
+} // namespace pel
+} // namespace attn
diff --git a/attn/pel/user_header.cpp b/attn/pel/user_header.cpp
new file mode 100644
index 0000000..23238e8
--- /dev/null
+++ b/attn/pel/user_header.cpp
@@ -0,0 +1,48 @@
+#include "user_header.hpp"
+
+namespace attn
+{
+namespace pel
+{
+
+UserHeader::UserHeader(Stream& pel)
+{
+ unflatten(pel);
+}
+
+void UserHeader::flatten(Stream& stream) const
+{
+ stream << _header << _eventSubsystem << _eventScope << _eventSeverity
+ << _eventType << _reserved4Byte1 << _problemDomain << _problemVector
+ << _actionFlags << _reserved4Byte2;
+}
+
+void UserHeader::unflatten(Stream& stream)
+{
+ stream >> _header >> _eventSubsystem >> _eventScope >> _eventSeverity >>
+ _eventType >> _reserved4Byte1 >> _problemDomain >> _problemVector >>
+ _actionFlags >> _reserved4Byte2;
+}
+
+void UserHeader::setSubsystem(uint8_t subsystem)
+{
+ _eventSubsystem = subsystem;
+}
+
+void UserHeader::setSeverity(uint8_t severity)
+{
+ _eventSeverity = severity;
+}
+
+void UserHeader::setType(uint8_t type)
+{
+ _eventType = type;
+}
+
+void UserHeader::setAction(uint16_t action)
+{
+ _actionFlags = action;
+}
+
+} // namespace pel
+} // namespace attn
diff --git a/attn/pel/user_header.hpp b/attn/pel/user_header.hpp
new file mode 100644
index 0000000..3be6e66
--- /dev/null
+++ b/attn/pel/user_header.hpp
@@ -0,0 +1,157 @@
+#pragma once
+
+#include "pel_common.hpp"
+#include "pel_section.hpp"
+#include "stream.hpp"
+
+namespace attn
+{
+namespace pel
+{
+
+/**
+ * @class UserHeader
+ *
+ * This represents the User Header section in a PEL.
+ *
+ * |--------+----------------+----------------+----------------+------------|
+ * | length | byte0 | byte1 | byte2 | byte3 |
+ * |--------+----------------+----------------+----------------+------------|
+ * | 8 | Section Header |
+ * | | |
+ * |--------+----------------+----------------+----------------+------------|
+ * | 4 | Subsystem ID | Event Scope | Event Severity | Event Type |
+ * |--------+----------------+----------------+----------------+------------|
+ * | 4 | reserved |
+ * |--------+----------------+----------------+-----------------------------|
+ * | 4 | Problem Domain | Problem Vector | Event Action Flags |
+ * |--------+----------------+----------------+-----------------------------|
+ * | 4 | reserved |
+ * |--------+---------------------------------------------------------------|
+ *
+ */
+class UserHeader : public Section
+{
+ public:
+ UserHeader() = delete;
+ ~UserHeader() = default;
+ UserHeader(const UserHeader&) = default;
+ UserHeader& operator=(const UserHeader&) = default;
+ UserHeader(UserHeader&&) = default;
+ UserHeader& operator=(UserHeader&&) = default;
+
+ /**
+ * @brief Constructor
+ *
+ * Fills in this class's data fields from raw data.
+ *
+ * @param[in] pel - the PEL data stream
+ */
+ explicit UserHeader(Stream& pel);
+
+ /**
+ * @brief Flatten the section into the stream
+ *
+ * @param[in] stream - The stream to write to
+ */
+ void flatten(Stream& stream) const override;
+
+ /**
+ * @brief Fills in the object from the stream data
+ *
+ * @param[in] stream - The stream to read from
+ */
+ void unflatten(Stream& stream);
+
+ /**
+ * @brief Returns the size of this section when flattened into a PEL
+ *
+ * @return size_t - the size of the section
+ */
+ static constexpr size_t flattenedSize()
+ {
+ return Section::flattenedSize() + sizeof(_eventSubsystem) +
+ sizeof(_eventScope) + sizeof(_eventSeverity) +
+ sizeof(_eventType) + sizeof(_reserved4Byte1) +
+ sizeof(_problemDomain) + sizeof(_problemVector) +
+ sizeof(_actionFlags) + sizeof(_reserved4Byte2);
+ }
+
+ /**
+ * @brief Set the subsystem field
+ *
+ * @param[in] subsystem - The subsystem value
+ */
+ void setSubsystem(uint8_t subsystem);
+
+ /**
+ * @brief Set the severity field
+ *
+ * @param[in] severity - The severity to set
+ */
+ void setSeverity(uint8_t severity);
+
+ /**
+ * @brief Set the event type field
+ *
+ * @param[in] type - The event type
+ */
+ void setType(uint8_t type);
+
+ /**
+ * @brief Set the action flags field
+ *
+ * @param[in] action - The action flags to set
+ */
+ void setAction(uint16_t action);
+
+ private:
+ /**
+ * @brief The subsystem associated with the event.
+ */
+ uint8_t _eventSubsystem;
+
+ /**
+ * @brief The event scope field.
+ */
+ uint8_t _eventScope = static_cast<uint8_t>(EventScope::platform);
+
+ /**
+ * @brief The event severity.
+ */
+ uint8_t _eventSeverity; // set by constructor
+
+ /**
+ * @brief The event type.
+ */
+ uint8_t _eventType = static_cast<uint8_t>(EventType::trace);
+
+ /**
+ * @brief A reserved 4 byte placeholder
+ */
+ uint32_t _reserved4Byte1 = 0;
+
+ /**
+ * @brief The problem domain field.
+ */
+ uint8_t _problemDomain = 0;
+
+ /**
+ * @brief The problem vector field.
+ */
+ uint8_t _problemVector = 0;
+
+ /**
+ * @brief The action flags field.
+ */
+ uint16_t _actionFlags; // set by contructor
+
+ /**
+ * @brief A reserved 4 byte
+ * placeholder
+ */
+ uint32_t _reserved4Byte2 = 0;
+};
+
+} // namespace pel
+} // namespace attn
diff --git a/attn/ti_handler.cpp b/attn/ti_handler.cpp
index 35fb346..e1dce7c 100644
--- a/attn/ti_handler.cpp
+++ b/attn/ti_handler.cpp
@@ -1,6 +1,7 @@
#include <attn/attn_common.hpp>
#include <attn/attn_handler.hpp>
#include <attn/attn_logging.hpp>
+#include <attn/pel/pel_common.hpp>
#include <attn/ti_handler.hpp>
#include <sdbusplus/bus.hpp>
#include <sdbusplus/exception.hpp>
@@ -17,7 +18,7 @@
* Use the TI info data area to determine if this is either a HB or a PHYP
* TI event then handle the event.
*
- * @param i_tiDataArea pointer to the TI infor data
+ * @param i_tiDataArea pointer to the TI info data
*/
int tiHandler(TiDataArea* i_tiDataArea)
{
@@ -84,10 +85,17 @@
if (nullptr != i_tiDataArea)
{
parsePhypOpalTiInfo(tiAdditionalData, i_tiDataArea);
- parseRawTiInfo(tiAdditionalData, i_tiDataArea);
}
- eventTerminate(tiAdditionalData); // generate PEL
+ tiAdditionalData["Subsystem"] =
+ std::to_string(static_cast<uint8_t>(pel::SubsystemID::hypervisor));
+
+ char srcChar[8];
+ memcpy(srcChar, &(i_tiDataArea->asciiData0), 4);
+ memcpy(&srcChar[4], &(i_tiDataArea->asciiData1), 4);
+ tiAdditionalData["SrcAscii"] = std::string{srcChar};
+
+ eventTerminate(tiAdditionalData, (char*)i_tiDataArea);
}
/**
@@ -207,42 +215,19 @@
if (nullptr != i_tiDataArea)
{
parseHbTiInfo(tiAdditionalData, i_tiDataArea);
- parseRawTiInfo(tiAdditionalData, i_tiDataArea);
}
if (true == generatePel)
{
- eventTerminate(tiAdditionalData); // generate PEL
- }
-}
+ tiAdditionalData["Subsystem"] =
+ std::to_string(static_cast<uint8_t>(pel::SubsystemID::hostboot));
-/** @brief Parse the TI info data area into map as raw 32-bit fields */
-void parseRawTiInfo(std::map<std::string, std::string>& i_map,
- TiDataArea* i_buffer)
-{
- if (nullptr == i_buffer)
- {
- return;
- }
+ char srcChar[8];
+ memcpy(srcChar, &(i_tiDataArea->srcWord12HbWord0), 4);
+ memcpy(&srcChar[4], &(i_tiDataArea->asciiData1), 4);
+ tiAdditionalData["SrcAscii"] = std::string{srcChar};
- uint32_t* tiDataArea = (uint32_t*)i_buffer;
- std::stringstream ss;
-
- ss << std::hex << std::setfill('0');
- ss << "raw:";
- while (tiDataArea <= (uint32_t*)((char*)i_buffer + sizeof(TiDataArea)))
- {
- ss << std::setw(8) << std::endl << be32toh(*tiDataArea);
- tiDataArea++;
- }
-
- std::string key, value;
- char delim = ':';
-
- while (std::getline(ss, key, delim))
- {
- std::getline(ss, value, delim);
- i_map[key] = value;
+ eventTerminate(tiAdditionalData, (char*)i_tiDataArea);
}
}
diff --git a/attn/ti_handler.hpp b/attn/ti_handler.hpp
index 33714a1..492868c 100644
--- a/attn/ti_handler.hpp
+++ b/attn/ti_handler.hpp
@@ -109,14 +109,6 @@
void handleHbTi(TiDataArea* i_tiDataArea);
/**
- * @brief Parse TI info data as raw 32-bit fields
- *
- * Read the TI data in as 32-bit fields and place into map.
- */
-void parseRawTiInfo(std::map<std::string, std::string>& i_map,
- TiDataArea* i_buffer);
-
-/**
* @brief Parse TI info data as PHYP/OPAL data
*
* Read the TI data, parse as PHYP/OPAL data and place into map.