PEL: Add ExtendedUserHeader section class

This is a required PEL section.

The section contains:
* The machine type/model/SN
* The server firmware version
* The BMC firmware version
* The 'Event Common Reference Time' (not used yet)
* The symptom ID (a unique event signature)

Signed-off-by: Matt Spinler <spinler@us.ibm.com>
Change-Id: I223041f85965195fccf69542dbe86ce856073b36
diff --git a/extensions/openpower-pels/extended_user_header.hpp b/extensions/openpower-pels/extended_user_header.hpp
new file mode 100644
index 0000000..422b3f5
--- /dev/null
+++ b/extensions/openpower-pels/extended_user_header.hpp
@@ -0,0 +1,254 @@
+#pragma once
+
+#include "bcd_time.hpp"
+#include "data_interface.hpp"
+#include "elog_entry.hpp"
+#include "mtms.hpp"
+#include "registry.hpp"
+#include "section.hpp"
+#include "src.hpp"
+#include "stream.hpp"
+
+namespace openpower
+{
+namespace pels
+{
+
+constexpr uint8_t extendedUserHeaderVersion = 0x01;
+constexpr size_t firmwareVersionSize = 16;
+
+/**
+ * @class ExtendedUserHeader
+ *
+ * This represents the Extended User Header section in a PEL.  It is  a required
+ * section.  It contains code versions, an MTMS subsection, and a string called
+ * a symptom ID.
+ *
+ * The Section base class handles the section header structure that every
+ * PEL section has at offset zero.
+ */
+class ExtendedUserHeader : public Section
+{
+  public:
+    ExtendedUserHeader() = delete;
+    ~ExtendedUserHeader() = default;
+    ExtendedUserHeader(const ExtendedUserHeader&) = default;
+    ExtendedUserHeader& operator=(const ExtendedUserHeader&) = default;
+    ExtendedUserHeader(ExtendedUserHeader&&) = default;
+    ExtendedUserHeader& operator=(ExtendedUserHeader&&) = default;
+
+    /**
+     * @brief Constructor
+     *
+     * Fills in this class's data fields from the stream.
+     *
+     * @param[in] pel - the PEL data stream
+     */
+    explicit ExtendedUserHeader(Stream& pel);
+
+    /**
+     * @brief Constructor
+     *
+     * @param[in] dataIface - The DataInterface object
+     * @param[in] regEntry - The message registry entry for this event
+     * @param[in] src - The SRC section object for this event
+     */
+    ExtendedUserHeader(const DataInterfaceBase& dataIface,
+                       const message::Entry& regEntry, const SRC& src);
+
+    /**
+     * @brief Flatten the section into the stream
+     *
+     * @param[in] stream - The stream to write to
+     */
+    void flatten(Stream& stream) const override;
+
+    /**
+     * @brief Returns the size of this section when flattened into a PEL
+     *
+     * @return size_t - the size of the section
+     */
+    size_t flattenedSize()
+    {
+        return Section::flattenedSize() + _mtms.flattenedSize() +
+               _serverFWVersion.size() + _subsystemFWVersion.size() +
+               sizeof(_reserved4B) + sizeof(_refTime) + sizeof(_reserved1B1) +
+               sizeof(_reserved1B2) + sizeof(_reserved1B3) +
+               sizeof(_symptomIDSize) + _symptomIDSize;
+    }
+
+    /**
+     * @brief Returns the server firmware version
+     *
+     * @return std::string - The version
+     */
+    std::string serverFWVersion() const
+    {
+        std::string version;
+        for (size_t i = 0;
+             i < _serverFWVersion.size() && _serverFWVersion[i] != '\0'; i++)
+        {
+            version.push_back(_serverFWVersion[i]);
+        }
+        return version;
+    }
+
+    /**
+     * @brief Returns the subsystem firmware version
+     *
+     * @return std::string - The version
+     */
+    std::string subsystemFWVersion() const
+    {
+        std::string version;
+        for (size_t i = 0;
+             i < _subsystemFWVersion.size() && _subsystemFWVersion[i] != '\0';
+             i++)
+        {
+            version.push_back(_subsystemFWVersion[i]);
+        }
+        return version;
+    }
+
+    /**
+     * @brief Returns the machine type+model
+     *
+     * @return std::string - The MTM
+     */
+    std::string machineTypeModel() const
+    {
+        return _mtms.machineTypeAndModel();
+    }
+
+    /**
+     * @brief Returns the machine serial number
+     *
+     * @return std::string - The serial number
+     */
+    std::string machineSerialNumber() const
+    {
+        return _mtms.machineSerialNumber();
+    }
+
+    /**
+     * @brief Returns the Event Common Reference Time field
+     *
+     * @return BCDTime - The time value
+     */
+    const BCDTime& refTime() const
+    {
+        return _refTime;
+    }
+
+    /**
+     * @brief Returns the symptom ID
+     *
+     * @return std::string - The symptom ID
+     */
+    std::string symptomID() const
+    {
+        std::string symptom;
+        for (size_t i = 0; i < _symptomID.size() && _symptomID[i] != '\0'; i++)
+        {
+            symptom.push_back(_symptomID[i]);
+        }
+        return symptom;
+    }
+
+  private:
+    /**
+     * @brief Fills in the object from the stream data
+     *
+     * @param[in] stream - The stream to read from
+     */
+    void unflatten(Stream& stream);
+
+    /**
+     * @brief Validates the section contents
+     *
+     * Updates _valid (in Section) with the results.
+     */
+    void validate() override;
+
+    /**
+     * @brief Builds the symptom ID
+     *
+     * Uses the message registry to say which SRC fields to add
+     * to the SRC's ASCII string to make the ID, and uses a smart
+     * default if not specified in the registry.
+     *
+     * @param[in] regEntry - The message registry entry for this event
+     * @param[in] src - The SRC section object for this event
+     */
+    void createSymptomID(const message::Entry& regEntry, const SRC& src);
+
+    /**
+     * @brief The structure that holds the machine TM and SN fields.
+     */
+    MTMS _mtms;
+
+    /**
+     * @brief The server firmware version
+     *
+     * NULL terminated.
+     *
+     * The release version of the full firmware image.
+     */
+    std::array<uint8_t, firmwareVersionSize> _serverFWVersion;
+
+    /**
+     * @brief The subsystem firmware version
+     *
+     * NULL terminated.
+     *
+     * On PELs created on the BMC, this will be the BMC code version.
+     */
+    std::array<uint8_t, firmwareVersionSize> _subsystemFWVersion;
+
+    /**
+     * @brief Reserved
+     */
+    uint32_t _reserved4B = 0;
+
+    /**
+     * @brief Event Common Reference Time
+     *
+     * This is not used by PELs created on the BMC.
+     */
+    BCDTime _refTime;
+
+    /**
+     * @brief Reserved
+     */
+    uint8_t _reserved1B1 = 0;
+
+    /**
+     * @brief Reserved
+     */
+    uint8_t _reserved1B2 = 0;
+
+    /**
+     * @brief Reserved
+     */
+    uint8_t _reserved1B3 = 0;
+
+    /**
+     * @brief The size of the symptom ID field
+     */
+    uint8_t _symptomIDSize;
+
+    /**
+     * @brief The symptom ID field
+     *
+     * Describes a unique event signature for the log.
+     * Required for serviceable events, otherwise optional.
+     * When present, must start with the first 8 characters
+     * of the ASCII string field from the SRC.
+     *
+     * NULL terminated.
+     */
+    std::vector<uint8_t> _symptomID;
+};
+
+} // namespace pels
+} // namespace openpower