summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatt Spinler <spinler@us.ibm.com>2019-07-16 15:58:51 -0500
committerMatt Spinler <spinler@us.ibm.com>2019-08-05 12:49:50 -0500
commitcb6b059eec52ddfe099221de003d7dca86ce1db0 (patch)
tree50d851101376d29413e438cffd226e877428b900
parentdf13bdb6e1423b25e7b737f4bf431af3d5c08d8b (diff)
downloadphosphor-logging-cb6b059eec52ddfe099221de003d7dca86ce1db0.tar.gz
phosphor-logging-cb6b059eec52ddfe099221de003d7dca86ce1db0.zip
PEL: Add PEL class
This class represents a Platform Event Log. A PEL consists of sections, and this commit just adds support for the only required sections - the Private Header and User Header, by including those specific objects. More will be added in the future. The only constructor provided in this commit is to construct the object from an existing flattened PEL buffer. This is for use in the case when a PEL is received from off the BMC, such as from the host. Future commits will add support for creating PELs from OpenBMC event logs. Since there aren't objects yet for every PEL section, the class cannot make a full flattened PEL without still keeping around the original PEL data it received in the constructor as mentioned above. So for now it will keep that data and just overlay the sections it does support when flattening. In the future, a fully formed PEL will be able to be constructed from just flattening the section objects in the correct order. This commit provides a few public methods of note: * data() - returns a flattened PEL * assignID() - sets a unique ID in the log ID field in the Private Header * setCommitTime() - Sets the commit timestamp in the Private Header to the current time * valid() - Says if the PEL is properly formed Signed-off-by: Matt Spinler <spinler@us.ibm.com> Change-Id: I2a9d82df9cd096ce77ecca7b2f73b097b8368aa2
-rw-r--r--extensions/openpower-pels/openpower-pels.mk1
-rw-r--r--extensions/openpower-pels/pel.cpp85
-rw-r--r--extensions/openpower-pels/pel.hpp204
-rw-r--r--test/openpower-pels/Makefile.include16
-rw-r--r--test/openpower-pels/pel_test.cpp111
-rw-r--r--test/openpower-pels/pel_utils.cpp2
-rw-r--r--test/openpower-pels/pel_utils.hpp22
7 files changed, 439 insertions, 2 deletions
diff --git a/extensions/openpower-pels/openpower-pels.mk b/extensions/openpower-pels/openpower-pels.mk
index 2cc2393..04106fd 100644
--- a/extensions/openpower-pels/openpower-pels.mk
+++ b/extensions/openpower-pels/openpower-pels.mk
@@ -4,5 +4,6 @@ phosphor_log_manager_SOURCES += \
extensions/openpower-pels/log_id.cpp \
extensions/openpower-pels/manager.cpp \
extensions/openpower-pels/paths.cpp \
+ extensions/openpower-pels/pel.cpp \
extensions/openpower-pels/private_header.cpp \
extensions/openpower-pels/user_header.cpp
diff --git a/extensions/openpower-pels/pel.cpp b/extensions/openpower-pels/pel.cpp
new file mode 100644
index 0000000..3000864
--- /dev/null
+++ b/extensions/openpower-pels/pel.cpp
@@ -0,0 +1,85 @@
+#include "pel.hpp"
+
+#include "bcd_time.hpp"
+#include "log_id.hpp"
+#include "stream.hpp"
+
+namespace openpower
+{
+namespace pels
+{
+
+PEL::PEL(const std::vector<uint8_t>& data) : PEL(data, 0)
+{
+}
+
+PEL::PEL(const std::vector<uint8_t>& data, uint32_t obmcLogID) : _rawPEL(data)
+{
+ populateFromRawData(obmcLogID);
+}
+
+void PEL::populateFromRawData(uint32_t obmcLogID)
+{
+ Stream pelData{_rawPEL};
+ _ph = std::make_unique<PrivateHeader>(pelData);
+ if (obmcLogID != 0)
+ {
+ _ph->obmcLogID() = obmcLogID;
+ }
+
+ _uh = std::make_unique<UserHeader>(pelData);
+}
+
+bool PEL::valid() const
+{
+ bool valid = _ph->valid();
+
+ if (valid)
+ {
+ valid = _uh->valid();
+ }
+
+ return valid;
+}
+
+void PEL::setCommitTime()
+{
+ auto now = std::chrono::system_clock::now();
+ _ph->commitTimestamp() = getBCDTime(now);
+}
+
+void PEL::assignID()
+{
+ _ph->id() = generatePELID();
+}
+
+void PEL::flatten(std::vector<uint8_t>& pelBuffer)
+{
+ Stream pelData{pelBuffer};
+ if (_ph->valid())
+ {
+ pelData << *_ph;
+ }
+ else
+ {
+ return;
+ }
+
+ if (_uh->valid())
+ {
+ pelData << *_uh;
+ }
+}
+
+std::vector<uint8_t> PEL::data()
+{
+ // Until we can recreate a complete PEL from objects, need to just flatten
+ // on top of the original PEL data which we need to keep around for this
+ // reason.
+
+ flatten(_rawPEL);
+ return _rawPEL;
+}
+
+} // namespace pels
+} // namespace openpower
diff --git a/extensions/openpower-pels/pel.hpp b/extensions/openpower-pels/pel.hpp
new file mode 100644
index 0000000..564fe33
--- /dev/null
+++ b/extensions/openpower-pels/pel.hpp
@@ -0,0 +1,204 @@
+#pragma once
+
+#include "private_header.hpp"
+#include "user_header.hpp"
+
+#include <memory>
+#include <vector>
+
+namespace openpower
+{
+namespace pels
+{
+
+/** @class PEL
+ *
+ * @brief This class represents a specific event log format referred to as a
+ * Platform Event Log.
+ *
+ * Every field in a PEL are in structures call sections, of which there are
+ * several types. Some sections are required, and some are optional. In some
+ * cases there may be more than one instance of a section type.
+ *
+ * The only two required sections for every type of PEL are the Private Header
+ * section and User Header section, which must be in the first and second
+ * positions, respectively.
+ *
+ * Every section starts with an 8 byte section header, which has the section
+ * size and type, among other things.
+ *
+ * This class represents all sections with objects.
+ *
+ * The available constructors are:
+ * - PEL(const std::vector<uint8_t>& data) - build this object out of a fully
+ * formed flattened PEL.
+ *
+ * The data() method allows one to retrieve the PEL as a vector<uint8_t>. This
+ * is the format in which it is stored and transmitted.
+ */
+class PEL
+{
+ public:
+ PEL() = delete;
+ ~PEL() = default;
+ PEL(const PEL&) = delete;
+ PEL& operator=(const PEL&) = delete;
+ PEL(PEL&&) = delete;
+ PEL& operator=(PEL&&) = delete;
+
+ /**
+ * @brief Constructor
+ *
+ * Build a PEL from raw data.
+ *
+ * @param[in] data - The PEL data
+ */
+ PEL(const std::vector<uint8_t>& data);
+
+ /**
+ * @brief Constructor
+ *
+ * Build a PEL from the raw data.
+ *
+ * @param[in] data - the PEL data
+ * @param[in] obmcLogID - the corresponding OpenBMC event log ID
+ */
+ PEL(const std::vector<uint8_t>& data, uint32_t obmcLogID);
+
+ /**
+ * @brief Convenience function to return the log ID field from the
+ * Private Header section.
+ *
+ * @return uint32_t - the ID
+ */
+ uint32_t id() const
+ {
+ return _ph->id();
+ }
+
+ /**
+ * @brief Convenience function to return the PLID field from the
+ * Private Header section.
+ *
+ * @return uint32_t - the PLID
+ */
+ uint32_t plid() const
+ {
+ return _ph->plid();
+ }
+
+ /**
+ * @brief Convenience function to return the OpenBMC event log ID field
+ * from the Private Header section.
+ *
+ * @return uint32_t - the OpenBMC event log ID
+ */
+ uint32_t obmcLogID() const
+ {
+ return _ph->obmcLogID();
+ }
+
+ /**
+ * @brief Convenience function to return the commit time field from
+ * the Private Header section.
+ *
+ * @return BCDTime - the timestamp
+ */
+ BCDTime commitTime() const
+ {
+ return _ph->commitTimestamp();
+ }
+
+ /**
+ * @brief Convenience function to return the create time field from
+ * the Private Header section.
+ *
+ * @return BCDTime - the timestamp
+ */
+ BCDTime createTime() const
+ {
+ return _ph->createTimestamp();
+ }
+
+ /**
+ * @brief Gives access to the Private Header section class
+ *
+ * @return std::unique_ptr<PrivateHeader>& the private header
+ */
+ std::unique_ptr<PrivateHeader>& privateHeader()
+ {
+ return _ph;
+ }
+
+ /**
+ * @brief Gives access to the User Header section class
+ *
+ * @return std::unique_ptr<UserHeader>& the user header
+ */
+ std::unique_ptr<UserHeader>& userHeader()
+ {
+ return _uh;
+ }
+
+ /**
+ * @brief Returns the PEL data.
+ *
+ * @return std::vector<uint8_t> - the raw PEL data
+ */
+ std::vector<uint8_t> data();
+
+ /**
+ * @brief Says if the PEL is valid (the sections are all valid)
+ *
+ * @return bool - if the PEL is valid
+ */
+ bool valid() const;
+
+ /**
+ * @brief Sets the commit timestamp to the current time
+ */
+ void setCommitTime();
+
+ /**
+ * @brief Sets the error log ID field to a unique ID.
+ */
+ void assignID();
+
+ private:
+ /**
+ * @brief Builds the section objects from a PEL data buffer
+ *
+ * @param[in] obmcLogID - The OpenBMC event log ID to use for that
+ * field in the Private Header.
+ */
+ void populateFromRawData(uint32_t obmcLogID);
+
+ /**
+ * @brief Flattens the PEL objects into the buffer
+ *
+ * @param[out] pelBuffer - What the data will be written to
+ */
+ void flatten(std::vector<uint8_t>& pelBuffer);
+
+ /**
+ * @brief The PEL Private Header section
+ */
+ std::unique_ptr<PrivateHeader> _ph;
+
+ /**
+ * @brief The PEL User Header section
+ */
+ std::unique_ptr<UserHeader> _uh;
+
+ /**
+ * @brief The PEL itself.
+ *
+ * This should be able to be removed when this class is able to
+ * serialize/deserialize a complete PEL from its objects, as
+ * then there will be no need to keep around the data anymore.
+ */
+ std::vector<uint8_t> _rawPEL;
+};
+
+} // namespace pels
+} // namespace openpower
diff --git a/test/openpower-pels/Makefile.include b/test/openpower-pels/Makefile.include
index 0e0257c..052aa2a 100644
--- a/test/openpower-pels/Makefile.include
+++ b/test/openpower-pels/Makefile.include
@@ -4,6 +4,7 @@ check_PROGRAMS += \
additional_data_test \
bcd_time_test \
log_id_test \
+ pel_test \
private_header_test \
section_header_test \
stream_test \
@@ -11,6 +12,8 @@ check_PROGRAMS += \
pel_objects = \
$(top_builddir)/extensions/openpower-pels/bcd_time.o \
+ $(top_builddir)/extensions/openpower-pels/log_id.o \
+ $(top_builddir)/extensions/openpower-pels/pel.o \
$(top_builddir)/extensions/openpower-pels/private_header.o \
$(top_builddir)/extensions/openpower-pels/user_header.o
@@ -43,7 +46,7 @@ section_header_test_LDADD = $(test_ldadd)
section_header_test_LDFLAGS = $(test_ldflags)
private_header_test_SOURCES = \
- %reldir%/private_header_test.cpp %reldir%/pel_utils.cpp
+ %reldir%/private_header_test.cpp %reldir%/pel_utils.cpp %reldir%/paths.cpp
private_header_test_CPPFLAGS = $(test_cppflags)
private_header_test_CXXFLAGS = $(test_cxxflags)
private_header_test_LDADD = \
@@ -52,7 +55,7 @@ private_header_test_LDADD = \
private_header_test_LDFLAGS = $(test_ldflags)
user_header_test_SOURCES = \
- %reldir%/user_header_test.cpp %reldir%/pel_utils.cpp
+ %reldir%/user_header_test.cpp %reldir%/pel_utils.cpp %reldir%/paths.cpp
user_header_test_CPPFLAGS = $(test_cppflags)
user_header_test_CXXFLAGS = $(test_cxxflags)
user_header_test_LDADD = \
@@ -68,3 +71,12 @@ log_id_test_LDADD = \
$(test_ldadd) \
$(top_builddir)/extensions/openpower-pels/log_id.o
log_id_test_LDFLAGS = $(test_ldflags)
+
+pel_test_SOURCES = \
+ %reldir%/pel_test.cpp %reldir%/paths.cpp %reldir%/pel_utils.cpp
+pel_test_CPPFLAGS = $(test_cppflags)
+pel_test_CXXFLAGS = $(test_cxxflags)
+pel_test_LDADD = \
+ $(test_ldadd) \
+ $(pel_objects)
+pel_test_LDFLAGS = $(test_ldflags)
diff --git a/test/openpower-pels/pel_test.cpp b/test/openpower-pels/pel_test.cpp
new file mode 100644
index 0000000..ba71dc2
--- /dev/null
+++ b/test/openpower-pels/pel_test.cpp
@@ -0,0 +1,111 @@
+#include "extensions/openpower-pels/pel.hpp"
+#include "pel_utils.hpp"
+
+#include <filesystem>
+#include <fstream>
+
+#include <gtest/gtest.h>
+
+namespace fs = std::filesystem;
+using namespace openpower::pels;
+
+class PELTest : public CleanLogID
+{
+};
+
+TEST_F(PELTest, FlattenTest)
+{
+ auto data = pelDataFactory(TestPelType::pelSimple);
+ auto pel = std::make_unique<PEL>(*data);
+
+ // Check a few fields
+ EXPECT_TRUE(pel->valid());
+ EXPECT_EQ(pel->id(), 0x80818283);
+ EXPECT_EQ(pel->plid(), 0x50515253);
+ EXPECT_EQ(pel->userHeader()->subsystem(), 0x10);
+ EXPECT_EQ(pel->userHeader()->actionFlags(), 0x80C0);
+
+ // Test that data in == data out
+ auto flattenedData = pel->data();
+ ASSERT_EQ(*data, flattenedData);
+}
+
+TEST_F(PELTest, CommitTimeTest)
+{
+ auto data = pelDataFactory(TestPelType::pelSimple);
+ auto pel = std::make_unique<PEL>(*data);
+
+ auto origTime = pel->commitTime();
+ pel->setCommitTime();
+ auto newTime = pel->commitTime();
+
+ ASSERT_NE(origTime, newTime);
+
+ // Make a new PEL and check new value is still there
+ auto newData = pel->data();
+ auto newPel = std::make_unique<PEL>(newData);
+ ASSERT_EQ(newTime, newPel->commitTime());
+}
+
+TEST_F(PELTest, AssignIDTest)
+{
+ auto data = pelDataFactory(TestPelType::pelSimple);
+ auto pel = std::make_unique<PEL>(*data);
+
+ auto origID = pel->id();
+ pel->assignID();
+ auto newID = pel->id();
+
+ ASSERT_NE(origID, newID);
+
+ // Make a new PEL and check new value is still there
+ auto newData = pel->data();
+ auto newPel = std::make_unique<PEL>(newData);
+ ASSERT_EQ(newID, newPel->id());
+}
+
+TEST_F(PELTest, WithLogIDTest)
+{
+ auto data = pelDataFactory(TestPelType::pelSimple);
+ auto pel = std::make_unique<PEL>(*data, 0x42);
+
+ EXPECT_TRUE(pel->valid());
+ EXPECT_EQ(pel->obmcLogID(), 0x42);
+}
+
+TEST_F(PELTest, InvalidPELTest)
+{
+ auto data = pelDataFactory(TestPelType::pelSimple);
+
+ // Too small
+ data->resize(PrivateHeader::flattenedSize());
+
+ auto pel = std::make_unique<PEL>(*data);
+
+ EXPECT_TRUE(pel->privateHeader()->valid());
+ EXPECT_FALSE(pel->userHeader()->valid());
+ EXPECT_FALSE(pel->valid());
+
+ // Ensure we can still flatten bad data
+ auto newData = pel->data();
+ EXPECT_EQ(*data, newData);
+
+ // Now corrupt the private header
+ data = pelDataFactory(TestPelType::pelSimple);
+ data->at(0) = 0;
+ pel = std::make_unique<PEL>(*data);
+
+ EXPECT_FALSE(pel->privateHeader()->valid());
+ EXPECT_TRUE(pel->userHeader()->valid());
+ EXPECT_FALSE(pel->valid());
+}
+
+TEST_F(PELTest, EmptyDataTest)
+{
+ std::vector<uint8_t> data;
+ auto pel = std::make_unique<PEL>(data);
+
+ EXPECT_FALSE(pel->privateHeader()->valid());
+ EXPECT_FALSE(pel->userHeader()->valid());
+ EXPECT_FALSE(pel->valid());
+}
diff --git a/test/openpower-pels/pel_utils.cpp b/test/openpower-pels/pel_utils.cpp
index b6714eb..4541694 100644
--- a/test/openpower-pels/pel_utils.cpp
+++ b/test/openpower-pels/pel_utils.cpp
@@ -10,6 +10,8 @@
namespace fs = std::filesystem;
using namespace openpower::pels;
+std::filesystem::path CleanLogID::pelIDFile{};
+
constexpr uint8_t simplePEL[] = {
// private header section header
0x50, 0x48, // ID 'PH'
diff --git a/test/openpower-pels/pel_utils.hpp b/test/openpower-pels/pel_utils.hpp
index 65fa196..7475720 100644
--- a/test/openpower-pels/pel_utils.hpp
+++ b/test/openpower-pels/pel_utils.hpp
@@ -1,3 +1,5 @@
+#include "extensions/openpower-pels/paths.hpp"
+
#include <filesystem>
#include <memory>
#include <vector>
@@ -5,6 +7,26 @@
#include <gtest/gtest.h>
/**
+ * @brief Test fixture to remove the pelID file that PELs use.
+ */
+class CleanLogID : public ::testing::Test
+{
+ protected:
+ static void SetUpTestCase()
+ {
+ pelIDFile = openpower::pels::getPELIDFile();
+ }
+
+ static void TearDownTestCase()
+ {
+ std::filesystem::remove_all(
+ std::filesystem::path{pelIDFile}.parent_path());
+ }
+
+ static std::filesystem::path pelIDFile;
+};
+
+/**
* @brief Tells the factory which PEL to create
*/
enum class TestPelType
OpenPOWER on IntegriCloud