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/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