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