PEL: Create a PEL that takes parameters for test

Make a pelFactory() function that unit tests can use where the caller
can specify the following attributes:
  * ID (PLID also set to this. BMC log ID = ID + 500)
  * Creator ID
  * Severity
  * Action Flags
  * PEL Size

The desired size is reached by adding a UserData section as the last
section.

Signed-off-by: Matt Spinler <spinler@us.ibm.com>
Change-Id: I99add4c17a352030298874340f8bf9d8041a3e74
diff --git a/test/openpower-pels/Makefile.include b/test/openpower-pels/Makefile.include
index 0ee298b..bc10b89 100644
--- a/test/openpower-pels/Makefile.include
+++ b/test/openpower-pels/Makefile.include
@@ -35,7 +35,6 @@
 
 pel_objects = \
 	$(top_builddir)/extensions/openpower-pels/ascii_string.o \
-	$(top_builddir)/extensions/openpower-pels/bcd_time.o \
 	$(top_builddir)/extensions/openpower-pels/callout.o \
 	$(top_builddir)/extensions/openpower-pels/callouts.o \
 	$(top_builddir)/extensions/openpower-pels/device_callouts.o \
@@ -60,6 +59,7 @@
 	$(top_builddir)/extensions/openpower-pels/user_header.o
 
 pel_test_utils_ldadd = \
+	$(top_builddir)/extensions/openpower-pels/bcd_time.o \
 	$(top_builddir)/test/openpower-pels/pel_utils.o \
 	$(top_builddir)/test/openpower-pels/paths.o
 
@@ -330,7 +330,6 @@
 	$(test_ldadd) \
 	$(pel_test_utils_ldadd) \
 	$(top_builddir)/extensions/openpower-pels/ascii_string.o \
-	$(top_builddir)/extensions/openpower-pels/bcd_time.o \
 	$(top_builddir)/extensions/openpower-pels/callout.o \
 	$(top_builddir)/extensions/openpower-pels/callouts.o \
 	$(top_builddir)/extensions/openpower-pels/device_callouts.o \
diff --git a/test/openpower-pels/pel_utils.cpp b/test/openpower-pels/pel_utils.cpp
index 4560b2f..cf7a75f 100644
--- a/test/openpower-pels/pel_utils.cpp
+++ b/test/openpower-pels/pel_utils.cpp
@@ -46,7 +46,7 @@
     0x90, 0x91, 0x92, 0x93,                         // OpenBMC log ID
     0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0,    // creator version
     0x50, 0x51, 0x52, 0x53,                         // plid
-    0x80, 0x81, 0x82, 0x83};
+    0x80, 0x81, 0x82, 0x83};                        // PEL ID
 
 const std::vector<uint8_t> userHeaderSection{
     // section header
@@ -146,6 +146,14 @@
 };
 
 constexpr size_t sectionCountOffset = 27;
+constexpr size_t createTimestampPHOffset = 8;
+constexpr size_t commitTimestampPHOffset = 16;
+constexpr size_t creatorPHOffset = 24;
+constexpr size_t obmcIDPHOffset = 28;
+constexpr size_t plidPHOffset = 40;
+constexpr size_t pelIDPHOffset = 44;
+constexpr size_t sevUHOffset = 10;
+constexpr size_t actionFlagsUHOffset = 18;
 
 std::vector<uint8_t> pelDataFactory(TestPELType type)
 {
@@ -216,6 +224,80 @@
     return data;
 }
 
+std::vector<uint8_t> pelFactory(uint32_t id, char creatorID, uint8_t severity,
+                                uint16_t actionFlags, size_t size)
+{
+    std::vector<uint8_t> data;
+    size_t offset = 0;
+
+    auto now = std::chrono::system_clock::now();
+    auto timestamp = getBCDTime(now);
+
+    // Start with the default Private Header, and modify it
+    data.insert(data.end(), privateHeaderSection.begin(),
+                privateHeaderSection.end());
+    data.at(creatorPHOffset) = creatorID;
+
+    // Modify the multibyte fields in it
+    Stream stream{data};
+    stream.offset(createTimestampPHOffset);
+    stream << timestamp;
+    stream.offset(commitTimestampPHOffset);
+    stream << timestamp;
+    stream.offset(plidPHOffset);
+    stream << id;
+    stream.offset(pelIDPHOffset);
+    stream << id;
+    stream.offset(obmcIDPHOffset);
+    stream << id + 500;
+
+    offset = data.size();
+
+    // User Header
+    data.insert(data.end(), userHeaderSection.begin(), userHeaderSection.end());
+    data.at(offset + sevUHOffset) = severity;
+    data.at(offset + actionFlagsUHOffset) = actionFlags >> 8;
+    data.at(offset + actionFlagsUHOffset + 1) = actionFlags;
+
+    // Use the default SRC, failing MTMS, and ext user Header sections
+    data.insert(data.end(), srcSectionNoCallouts.begin(),
+                srcSectionNoCallouts.end());
+    data.insert(data.end(), failingMTMSSection.begin(),
+                failingMTMSSection.end());
+    data.insert(data.end(), ExtUserHeaderSection.begin(),
+                ExtUserHeaderSection.end());
+
+    data.at(sectionCountOffset) = 5;
+
+    // Require the size to be enough for all the above sections.
+    assert(size >= data.size());
+    assert(size <= 16384);
+
+    // Add a UserData section to get the size we need.
+    auto udSection = UserDataSection;
+    udSection.resize(size - data.size());
+
+    if (!udSection.empty())
+    {
+        // At least has to be 8B for the header
+        assert(udSection.size() >= 8);
+
+        // UD sections must be 4B aligned
+        assert(udSection.size() % 4 == 0);
+
+        // Set the new size in the section heder
+        Stream udStream{udSection};
+        udStream.offset(2);
+        udStream << static_cast<uint16_t>(udSection.size());
+
+        data.insert(data.end(), udSection.begin(), udSection.end());
+        data[sectionCountOffset]++;
+    }
+
+    assert(size == data.size());
+    return data;
+}
+
 std::vector<uint8_t> srcDataFactory(TestSRCType type)
 {
     switch (type)
diff --git a/test/openpower-pels/pel_utils.hpp b/test/openpower-pels/pel_utils.hpp
index fb7662e..1c4eebb 100644
--- a/test/openpower-pels/pel_utils.hpp
+++ b/test/openpower-pels/pel_utils.hpp
@@ -85,6 +85,27 @@
 std::vector<uint8_t> pelDataFactory(TestPELType type);
 
 /**
+ * @brief PEL factory to create a PEL with the specified fields.
+ *
+ * The size is obtained by adding a UserData section of
+ * the size necessary after adding the 5 required sections.
+ *
+ * @param[in] id - The desired PEL ID
+ * @param[in] creatorID - The desired creator ID
+ * @param[in] severity - The desired severity
+ * @param[in] actionFlags - The desired action flags
+ * @param[in] size - The desired size.
+ *                   Must be:
+ *                     * 4B aligned
+ *                     * min 276 (size of the 5 required sections)
+ *                     * max 16834
+ *
+ * @return std::vector<uint8_t> - The PEL data
+ */
+std::vector<uint8_t> pelFactory(uint32_t id, char creatorID, uint8_t severity,
+                                uint16_t actionFlags, size_t size);
+
+/**
  * @brief SRC data factory, for testing
  *
  * Provides pieces of the SRC PEL section, such as a callout.