PEL: Add UserHeader class

The second section in a PEL is always the 'User Header' section.  This
commit adds a class to represent that.  Right now, the only constructor
available is filling in its data fields from a PEL stream.

Several of the fields in this section have predefined values that are
defined by the PEL specification.  Defining any constants or enums for
those will be left to future commits where they will actually be used.

Signed-off-by: Matt Spinler <spinler@us.ibm.com>
Change-Id: I8b5f856a4284d44c31b04e98a664f20cd8fa0cb6
diff --git a/test/openpower-pels/Makefile.include b/test/openpower-pels/Makefile.include
index 288a5db..f860db2 100644
--- a/test/openpower-pels/Makefile.include
+++ b/test/openpower-pels/Makefile.include
@@ -5,11 +5,13 @@
 	bcd_time_test \
 	private_header_test \
 	section_header_test \
-	stream_test
+	stream_test \
+	user_header_test
 
 pel_objects = \
 	$(top_builddir)/extensions/openpower-pels/bcd_time.o \
-	$(top_builddir)/extensions/openpower-pels/private_header.o
+	$(top_builddir)/extensions/openpower-pels/private_header.o \
+	$(top_builddir)/extensions/openpower-pels/user_header.o
 
 additional_data_test_SOURCES = %reldir%/additional_data_test.cpp
 additional_data_test_CPPFLAGS = $(test_cppflags)
@@ -47,3 +49,12 @@
 	$(test_ldadd) \
 	$(pel_objects)
 private_header_test_LDFLAGS = $(test_ldflags)
+
+user_header_test_SOURCES = \
+	%reldir%/user_header_test.cpp %reldir%/pel_utils.cpp
+user_header_test_CPPFLAGS = $(test_cppflags)
+user_header_test_CXXFLAGS = $(test_cxxflags)
+user_header_test_LDADD = \
+	$(test_ldadd) \
+	$(pel_objects)
+user_header_test_LDFLAGS = $(test_ldflags)
diff --git a/test/openpower-pels/pel_utils.cpp b/test/openpower-pels/pel_utils.cpp
index 4e4c32a..b6714eb 100644
--- a/test/openpower-pels/pel_utils.cpp
+++ b/test/openpower-pels/pel_utils.cpp
@@ -1,6 +1,7 @@
 #include "pel_utils.hpp"
 
 #include "extensions/openpower-pels/private_header.hpp"
+#include "extensions/openpower-pels/user_header.hpp"
 
 #include <fstream>
 
@@ -26,8 +27,23 @@
     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                          // id
+    0x80, 0x81, 0x82, 0x83,                         // id
 
+    // user header section header
+    0x55, 0x48, // ID 'UH'
+    0x00, 0x18, // Size
+    0x01, 0x0A, // version, subtype
+    0x0B, 0x0C, // comp ID
+
+    // user header
+    0x10, 0x04,             // subsystem, scope
+    0x20, 0x00,             // severity, type
+    0x00, 0x00, 0x00, 0x00, // reserved
+    0x03, 0x04,             // problem domain, vector
+    0x80, 0xC0,             // action flags
+    0x00, 0x00, 0x00, 0x00  // reserved
+
+    // Add more as the code supports more
 };
 
 std::unique_ptr<std::vector<uint8_t>> pelDataFactory(TestPelType type)
@@ -43,6 +59,12 @@
             data = std::make_unique<std::vector<uint8_t>>(
                 simplePEL, simplePEL + PrivateHeader::flattenedSize());
             break;
+        case TestPelType::userHeaderSimple:
+            data = std::make_unique<std::vector<uint8_t>>(
+                simplePEL + PrivateHeader::flattenedSize(),
+                simplePEL + PrivateHeader::flattenedSize() +
+                    UserHeader::flattenedSize());
+            break;
     }
     return data;
 }
diff --git a/test/openpower-pels/pel_utils.hpp b/test/openpower-pels/pel_utils.hpp
index bc4e05d..65fa196 100644
--- a/test/openpower-pels/pel_utils.hpp
+++ b/test/openpower-pels/pel_utils.hpp
@@ -10,7 +10,8 @@
 enum class TestPelType
 {
     pelSimple,
-    privateHeaderSimple
+    privateHeaderSimple,
+    userHeaderSimple
 };
 
 /**
diff --git a/test/openpower-pels/user_header_test.cpp b/test/openpower-pels/user_header_test.cpp
new file mode 100644
index 0000000..52087b6
--- /dev/null
+++ b/test/openpower-pels/user_header_test.cpp
@@ -0,0 +1,93 @@
+#include "extensions/openpower-pels/private_header.hpp"
+#include "extensions/openpower-pels/user_header.hpp"
+#include "pel_utils.hpp"
+
+#include <gtest/gtest.h>
+
+using namespace openpower::pels;
+
+TEST(UserHeaderTest, SizeTest)
+{
+    EXPECT_EQ(UserHeader::flattenedSize(), 24);
+}
+
+TEST(UserHeaderTest, UnflattenFlattenTest)
+{
+    auto data = pelDataFactory(TestPelType::userHeaderSimple);
+
+    Stream stream(*data);
+    UserHeader uh(stream);
+    EXPECT_EQ(uh.valid(), true);
+
+    EXPECT_EQ(uh.header().id, 0x5548);
+    EXPECT_EQ(uh.header().size, UserHeader::flattenedSize());
+    EXPECT_EQ(uh.header().version, 0x01);
+    EXPECT_EQ(uh.header().subType, 0x0A);
+    EXPECT_EQ(uh.header().componentID, 0x0B0C);
+
+    EXPECT_EQ(uh.subsystem(), 0x10);
+    EXPECT_EQ(uh.scope(), 0x04);
+    EXPECT_EQ(uh.severity(), 0x20);
+    EXPECT_EQ(uh.eventType(), 0x00);
+    EXPECT_EQ(uh.problemDomain(), 0x03);
+    EXPECT_EQ(uh.problemVector(), 0x04);
+    EXPECT_EQ(uh.actionFlags(), 0x80C0);
+
+    // Now flatten into a vector and check that this vector
+    // matches the original one.
+    std::vector<uint8_t> newData;
+    Stream newStream(newData);
+
+    newStream << uh;
+    EXPECT_EQ(*data, newData);
+
+    // Change a field, then flatten and unflatten again
+    uh.subsystem() = 0x44;
+
+    newStream.offset(0);
+    newData.clear();
+    newStream << uh;
+    EXPECT_NE(*data, newData);
+
+    newStream.offset(0);
+    UserHeader newUH(newStream);
+
+    EXPECT_TRUE(newUH.valid());
+    EXPECT_EQ(newUH.subsystem(), 0x44);
+}
+
+TEST(UserHeaderTest, ShortDataTest)
+{
+    auto data = pelDataFactory(TestPelType::userHeaderSimple);
+    data->resize(data->size() - 1);
+
+    Stream stream(*data);
+    UserHeader uh(stream);
+
+    EXPECT_EQ(uh.valid(), false);
+}
+
+TEST(UserHeaderTest, CorruptDataTest1)
+{
+    auto data = pelDataFactory(TestPelType::userHeaderSimple);
+    data->resize(data->size() - 1);
+
+    data->at(0) = 0; // corrupt the section ID
+
+    Stream stream(*data);
+    UserHeader uh(stream);
+
+    EXPECT_EQ(uh.valid(), false);
+}
+
+TEST(UserHeaderTest, CorruptDataTest2)
+{
+    auto data = pelDataFactory(TestPelType::userHeaderSimple);
+
+    data->at(4) = 0x22; // corrupt the version
+
+    Stream stream(*data);
+    UserHeader uh(stream);
+
+    EXPECT_EQ(uh.valid(), false);
+}