summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatt Spinler <spinler@us.ibm.com>2019-10-09 11:27:20 -0500
committerMatt Spinler <spinler@us.ibm.com>2019-10-22 14:09:56 +0000
commit6c9662c9a269a3fcf6e6ccee51b2ab76b1679fb0 (patch)
tree5ec7a8ad5e54d099e89e90bf057f458c412585fe
parent90b4a0a0682cb596fdaf0eabbaccd2735e52bd89 (diff)
downloadphosphor-logging-6c9662c9a269a3fcf6e6ccee51b2ab76b1679fb0.tar.gz
phosphor-logging-6c9662c9a269a3fcf6e6ccee51b2ab76b1679fb0.zip
PEL: SRC FRU callout structure
This class represents a single FRU callout in the SRC section of a PEL. When there are multiple callouts, there will be multiple instances of this class created. Technically, the callout isn't always a FRU, such as it could be a maintenance procedure name, but the spec still refers to this section as the FRU callout section. The callout priority and location code are in this structure. There can also be up to one each of three types of substructures in a single callout: * FRU Identity (must be first if present) * Power Controlling Enclosure (PCE) * Manufacturing Replaceable Unit (MRU) This commit just provides support for creating this object from a flattened PEL, such as one that comes down from the host. A future commit will add support for creating a callout for BMC created event logs. Signed-off-by: Matt Spinler <spinler@us.ibm.com> Change-Id: I49535285e3cbaa15dfe031648cfaf262380a1cf7
-rw-r--r--extensions/openpower-pels/callout.cpp102
-rw-r--r--extensions/openpower-pels/callout.hpp173
-rw-r--r--extensions/openpower-pels/openpower-pels.mk1
-rw-r--r--test/openpower-pels/Makefile.include15
-rw-r--r--test/openpower-pels/pel_utils.cpp43
-rw-r--r--test/openpower-pels/pel_utils.hpp21
-rw-r--r--test/openpower-pels/src_callout_test.cpp146
7 files changed, 501 insertions, 0 deletions
diff --git a/extensions/openpower-pels/callout.cpp b/extensions/openpower-pels/callout.cpp
new file mode 100644
index 0000000..53fe85d
--- /dev/null
+++ b/extensions/openpower-pels/callout.cpp
@@ -0,0 +1,102 @@
+#include "callout.hpp"
+
+#include <phosphor-logging/log.hpp>
+
+namespace openpower
+{
+namespace pels
+{
+namespace src
+{
+
+using namespace phosphor::logging;
+
+Callout::Callout(Stream& pel)
+{
+ pel >> _size >> _flags >> _priority >> _locationCodeSize;
+
+ if (_locationCodeSize)
+ {
+ _locationCode.resize(_locationCodeSize);
+ pel >> _locationCode;
+ }
+
+ size_t currentSize = 4 + _locationCodeSize;
+
+ // Read in the substructures until the end of this structure.
+ // Any stream overflows will throw an exception up to the SRC constructor
+ while (_size > currentSize)
+ {
+ // Peek the type
+ uint16_t type = 0;
+ pel >> type;
+ pel.offset(pel.offset() - 2);
+
+ switch (type)
+ {
+ case FRUIdentity::substructureType:
+ {
+ _fruIdentity = std::make_unique<FRUIdentity>(pel);
+ currentSize += _fruIdentity->flattenedSize();
+ break;
+ }
+ case PCEIdentity::substructureType:
+ {
+ _pceIdentity = std::make_unique<PCEIdentity>(pel);
+ currentSize += _pceIdentity->flattenedSize();
+ break;
+ }
+ case MRU::substructureType:
+ {
+ _mru = std::make_unique<MRU>(pel);
+ currentSize += _mru->flattenedSize();
+ break;
+ }
+ default:
+ log<level::ERR>("Invalid Callout subsection type",
+ entry("CALLOUT_TYPE=0x%X", type));
+ throw std::runtime_error("Invalid Callout subsection type");
+ break;
+ }
+ }
+}
+
+size_t Callout::flattenedSize()
+{
+ size_t size = sizeof(_size) + sizeof(_flags) + sizeof(_priority) +
+ sizeof(_locationCodeSize) + _locationCodeSize;
+
+ size += _fruIdentity ? _fruIdentity->flattenedSize() : 0;
+ size += _pceIdentity ? _pceIdentity->flattenedSize() : 0;
+ size += _mru ? _mru->flattenedSize() : 0;
+
+ return size;
+}
+
+void Callout::flatten(Stream& pel)
+{
+ pel << _size << _flags << _priority << _locationCodeSize;
+
+ if (_locationCodeSize)
+ {
+ pel << _locationCode;
+ }
+
+ if (_fruIdentity)
+ {
+ _fruIdentity->flatten(pel);
+ }
+
+ if (_pceIdentity)
+ {
+ _pceIdentity->flatten(pel);
+ }
+ if (_mru)
+ {
+ _mru->flatten(pel);
+ }
+}
+
+} // namespace src
+} // namespace pels
+} // namespace openpower
diff --git a/extensions/openpower-pels/callout.hpp b/extensions/openpower-pels/callout.hpp
new file mode 100644
index 0000000..ea28b7b
--- /dev/null
+++ b/extensions/openpower-pels/callout.hpp
@@ -0,0 +1,173 @@
+#pragma once
+
+#include "fru_identity.hpp"
+#include "mru.hpp"
+#include "pce_identity.hpp"
+#include "stream.hpp"
+
+namespace openpower
+{
+namespace pels
+{
+namespace src
+{
+
+/**
+ * @class Callout
+ *
+ * Represents a single FRU callout in the SRC's FRU callout
+ * subsection.
+ *
+ * The 'Callouts' class holds a list of these objects.
+ *
+ * The callout priority and location code are in this structure.
+ *
+ * There can also be up to one each of three types of substructures
+ * in a single callout:
+ * * FRU Identity (must be first if present)
+ * * Power Controlling Enclosure (PCE)
+ * * Manufacturing Replaceable Unit (MRU)
+ *
+ * These substructures have their own objects managed by unique_ptrs
+ * which will only be allocated if those substructures exist.
+ */
+class Callout
+{
+ public:
+ Callout() = delete;
+ ~Callout() = default;
+ Callout(const Callout&) = delete;
+ Callout& operator=(const Callout&) = delete;
+ Callout(Callout&&) = delete;
+ Callout& operator=(Callout&&) = delete;
+
+ /**
+ * @brief Constructor
+ *
+ * Fills in this class's data fields from the stream.
+ *
+ * @param[in] pel - the PEL data stream
+ */
+ explicit Callout(Stream& pel);
+
+ /**
+ * @brief Returns the size of this object when flattened into a PEL
+ *
+ * @return size_t - The size of the section
+ */
+ size_t flattenedSize();
+
+ /**
+ * @brief Flatten the object into the stream
+ *
+ * @param[in] stream - The stream to write to
+ */
+ void flatten(Stream& pel);
+
+ /**
+ * @brief Returns the priority field of a callout
+ *
+ * @return uint8_t - The priority
+ */
+ uint8_t priority() const
+ {
+ return _priority;
+ }
+
+ /**
+ * @brief Returns the location code of the callout
+ *
+ * @return std::string - The location code
+ */
+ std::string locationCode() const
+ {
+ std::string lc;
+ if (!_locationCode.empty())
+ {
+ // NULL terminated
+ lc = static_cast<const char*>(_locationCode.data());
+ }
+ return lc;
+ }
+
+ /**
+ * @brief Returns the FRU identity substructure
+ *
+ * @return const std::unique_ptr<FRUIdentity>&
+ */
+ const std::unique_ptr<FRUIdentity>& fruIdentity() const
+ {
+ return _fruIdentity;
+ }
+
+ /**
+ * @brief Returns the PCE identity substructure
+ *
+ * @return const std::unique_ptr<PCEIdentity>&
+ */
+ const std::unique_ptr<PCEIdentity>& pceIdentity() const
+ {
+ return _pceIdentity;
+ }
+
+ /**
+ * @brief Returns the MRU identity substructure
+ *
+ * @return const std::unique_ptr<FRUIdentity>&
+ */
+ const std::unique_ptr<MRU>& mru() const
+ {
+ return _mru;
+ }
+
+ private:
+ /**
+ * @brief The size of this structure in the PEL
+ */
+ uint8_t _size;
+
+ /**
+ * @brief The flags byte of this structure
+ */
+ uint8_t _flags;
+
+ /**
+ * @brief The replacement priority
+ */
+ uint8_t _priority;
+
+ /**
+ * @brief The length of the location code field.
+ *
+ * Includes the NULL termination, and must be a
+ * multiple of 4 (padded with zeros)
+ */
+ uint8_t _locationCodeSize;
+
+ /**
+ * @brief NULL terminated location code
+ *
+ * Includes the NULL termination, and must be a
+ * multiple of 4 (padded with zeros)
+ */
+ std::vector<char> _locationCode;
+
+ /**
+ * @brief FRU (Field Replaceable Unit) Identity substructure
+ */
+ std::unique_ptr<FRUIdentity> _fruIdentity;
+
+ /**
+ * @brief PCE (Power Controlling Enclosure) Identity substructure
+ */
+ std::unique_ptr<PCEIdentity> _pceIdentity;
+
+ /**
+ * @brief MRU (Manufacturing Replaceable Unit) substructure
+ */
+ std::unique_ptr<MRU> _mru;
+};
+
+} // namespace src
+} // namespace pels
+} // namespace openpower
diff --git a/extensions/openpower-pels/openpower-pels.mk b/extensions/openpower-pels/openpower-pels.mk
index fdc998d..9308159 100644
--- a/extensions/openpower-pels/openpower-pels.mk
+++ b/extensions/openpower-pels/openpower-pels.mk
@@ -1,6 +1,7 @@
phosphor_log_manager_SOURCES += \
extensions/openpower-pels/ascii_string.cpp \
extensions/openpower-pels/bcd_time.cpp \
+ extensions/openpower-pels/callout.cpp \
extensions/openpower-pels/data_interface.cpp \
extensions/openpower-pels/entry_points.cpp \
extensions/openpower-pels/failing_mtms.cpp \
diff --git a/test/openpower-pels/Makefile.include b/test/openpower-pels/Makefile.include
index e54bce8..ccd5d26 100644
--- a/test/openpower-pels/Makefile.include
+++ b/test/openpower-pels/Makefile.include
@@ -20,6 +20,7 @@ check_PROGRAMS += \
repository_test \
section_header_test \
severity_test \
+ src_callout_test \
stream_test \
user_data_test \
user_header_test
@@ -227,3 +228,17 @@ mru_test_LDADD = \
$(test_ldadd) \
$(top_builddir)/extensions/openpower-pels/mru.o
mru_test_LDFLAGS = $(test_ldflags)
+
+src_callout_test_SOURCES = \
+ %reldir%/src_callout_test.cpp \
+ %reldir%/pel_utils.cpp
+src_callout_test_CPPFLAGS = $(test_cppflags)
+src_callout_test_CXXFLAGS = $(test_cxxflags)
+src_callout_test_LDADD = \
+ $(test_ldadd) \
+ $(top_builddir)/extensions/openpower-pels/callout.o \
+ $(top_builddir)/extensions/openpower-pels/fru_identity.o \
+ $(top_builddir)/extensions/openpower-pels/mru.o \
+ $(top_builddir)/extensions/openpower-pels/mtms.o \
+ $(top_builddir)/extensions/openpower-pels/pce_identity.o
+src_callout_test_LDFLAGS = $(test_ldflags)
diff --git a/test/openpower-pels/pel_utils.cpp b/test/openpower-pels/pel_utils.cpp
index 3e0ac01..3610dd7 100644
--- a/test/openpower-pels/pel_utils.cpp
+++ b/test/openpower-pels/pel_utils.cpp
@@ -51,6 +51,33 @@ constexpr uint8_t simplePEL[] = {
// Add more as the code supports more
};
+std::vector<uint8_t> srcFRUIdentityCallout{
+ 'I', 'D', 0x1C, 0x1D, // type, size, flags
+ '1', '2', '3', '4', // PN
+ '5', '6', '7', 0x00, 'A', 'A', 'A', 'A', // CCIN
+ '1', '2', '3', '4', '5', '6', '7', '8', // SN
+ '9', 'A', 'B', 'C'};
+
+std::vector<uint8_t> srcPCEIdentityCallout{
+ 'P', 'E', 0x24, 0x00, // type, size, flags
+ 'T', 'T', 'T', 'T', '-', 'M', 'M', 'M', // MTM
+ '1', '2', '3', '4', '5', '6', '7', // SN
+ '8', '9', 'A', 'B', 'C', 'P', 'C', 'E', // Name + null padded
+ 'N', 'A', 'M', 'E', '1', '2', 0x00, 0x00, 0x00};
+
+std::vector<uint8_t> srcMRUCallout{
+ 'M', 'R', 0x28, 0x04, // ID, size, flags
+ 0x00, 0x00, 0x00, 0x00, // Reserved
+ 0x00, 0x00, 0x00, 'H', // priority 0
+ 0x01, 0x01, 0x01, 0x01, // MRU ID 0
+ 0x00, 0x00, 0x00, 'M', // priority 1
+ 0x02, 0x02, 0x02, 0x02, // MRU ID 1
+ 0x00, 0x00, 0x00, 'L', // priority 2
+ 0x03, 0x03, 0x03, 0x03, // MRU ID 2
+ 0x00, 0x00, 0x00, 'H', // priority 3
+ 0x04, 0x04, 0x04, 0x04, // MRU ID 3
+};
+
std::unique_ptr<std::vector<uint8_t>> pelDataFactory(TestPelType type)
{
std::unique_ptr<std::vector<uint8_t>> data;
@@ -74,6 +101,22 @@ std::unique_ptr<std::vector<uint8_t>> pelDataFactory(TestPelType type)
return data;
}
+std::vector<uint8_t> srcDataFactory(TestSRCType type)
+{
+ switch (type)
+ {
+ case TestSRCType::fruIdentityStructure:
+ return srcFRUIdentityCallout;
+
+ case TestSRCType::pceIdentityStructure:
+ return srcPCEIdentityCallout;
+
+ case TestSRCType::mruStructure:
+ return srcMRUCallout;
+ }
+ return {};
+}
+
std::unique_ptr<std::vector<uint8_t>> readPELFile(const fs::path& path)
{
std::ifstream file{path};
diff --git a/test/openpower-pels/pel_utils.hpp b/test/openpower-pels/pel_utils.hpp
index e61bea2..2d40033 100644
--- a/test/openpower-pels/pel_utils.hpp
+++ b/test/openpower-pels/pel_utils.hpp
@@ -60,6 +60,16 @@ enum class TestPelType
};
/**
+ * @brief Tells the SRC factory which data to create
+ */
+enum class TestSRCType
+{
+ fruIdentityStructure,
+ pceIdentityStructure,
+ mruStructure,
+};
+
+/**
* @brief PEL data factory, for testing
*
* @param[in] type - the type of data to create
@@ -69,6 +79,17 @@ enum class TestPelType
std::unique_ptr<std::vector<uint8_t>> pelDataFactory(TestPelType type);
/**
+ * @brief SRC data factory, for testing
+ *
+ * Provides pieces of the SRC PEL section, such as a callout.
+ *
+ * @param[in] type - the type of data to create
+ *
+ * @return std::vector<uint8_t> - The SRC data
+ */
+std::vector<uint8_t> srcDataFactory(TestSRCType type);
+
+/**
* @brief Helper function to read raw PEL data from a file
*
* @param[in] path - the path to read
diff --git a/test/openpower-pels/src_callout_test.cpp b/test/openpower-pels/src_callout_test.cpp
new file mode 100644
index 0000000..0a78c94
--- /dev/null
+++ b/test/openpower-pels/src_callout_test.cpp
@@ -0,0 +1,146 @@
+#include "extensions/openpower-pels/callout.hpp"
+#include "pel_utils.hpp"
+
+#include <gtest/gtest.h>
+
+using namespace openpower::pels;
+using namespace openpower::pels::src;
+
+// Unflatten the callout section with all three substructures
+TEST(CalloutTest, TestUnflattenAllSubstructures)
+{
+ // The base data.
+ std::vector<uint8_t> data{
+ 0xFF, 0x2F, 'H', 8, // size, flags, priority, LC length
+ 'U', '1', '2', '-', 'P', '1', 0x00, 0x00 // LC
+ };
+
+ auto fruIdentity = srcDataFactory(TestSRCType::fruIdentityStructure);
+ auto pceIdentity = srcDataFactory(TestSRCType::pceIdentityStructure);
+ auto mrus = srcDataFactory(TestSRCType::mruStructure);
+
+ // Add all 3 substructures
+ data.insert(data.end(), fruIdentity.begin(), fruIdentity.end());
+ data.insert(data.end(), pceIdentity.begin(), pceIdentity.end());
+ data.insert(data.end(), mrus.begin(), mrus.end());
+
+ // The final size
+ data[0] = data.size();
+
+ Stream stream{data};
+ Callout callout{stream};
+
+ EXPECT_EQ(callout.flattenedSize(), data.size());
+ EXPECT_EQ(callout.priority(), 'H');
+ EXPECT_EQ(callout.locationCode(), "U12-P1");
+
+ // Spot check the 3 substructures
+ EXPECT_TRUE(callout.fruIdentity());
+ EXPECT_EQ(callout.fruIdentity()->getSN(), "123456789ABC");
+
+ EXPECT_TRUE(callout.pceIdentity());
+ EXPECT_EQ(callout.pceIdentity()->enclosureName(), "PCENAME12");
+
+ EXPECT_TRUE(callout.mru());
+ EXPECT_EQ(callout.mru()->mrus().size(), 4);
+ EXPECT_EQ(callout.mru()->mrus().at(3).id, 0x04040404);
+
+ // Now flatten
+ std::vector<uint8_t> newData;
+ Stream newStream{newData};
+
+ callout.flatten(newStream);
+ EXPECT_EQ(data, newData);
+}
+
+TEST(CalloutTest, TestUnflattenOneSubstructure)
+{
+ std::vector<uint8_t> data{
+ 0xFF, 0x28, 'H', 0x08, // size, flags, priority, LC length
+ 'U', '1', '2', '-', 'P', '1', 0x00, 0x00 // LC
+ };
+
+ auto fruIdentity = srcDataFactory(TestSRCType::fruIdentityStructure);
+
+ data.insert(data.end(), fruIdentity.begin(), fruIdentity.end());
+
+ // The final size
+ data[0] = data.size();
+
+ Stream stream{data};
+ Callout callout{stream};
+
+ EXPECT_EQ(callout.flattenedSize(), data.size());
+
+ // Spot check the substructure
+ EXPECT_TRUE(callout.fruIdentity());
+ EXPECT_EQ(callout.fruIdentity()->getSN(), "123456789ABC");
+
+ // Not present
+ EXPECT_FALSE(callout.pceIdentity());
+ EXPECT_FALSE(callout.mru());
+
+ // Now flatten
+ std::vector<uint8_t> newData;
+ Stream newStream{newData};
+
+ callout.flatten(newStream);
+ EXPECT_EQ(data, newData);
+}
+
+TEST(CalloutTest, TestUnflattenTwoSubstructures)
+{
+ std::vector<uint8_t> data{
+ 0xFF, 0x2B, 'H', 0x08, // size, flags, priority, LC length
+ 'U', '1', '2', '-', 'P', '1', 0x00, 0x00 // LC
+ };
+
+ auto fruIdentity = srcDataFactory(TestSRCType::fruIdentityStructure);
+ auto pceIdentity = srcDataFactory(TestSRCType::pceIdentityStructure);
+
+ data.insert(data.end(), fruIdentity.begin(), fruIdentity.end());
+ data.insert(data.end(), pceIdentity.begin(), pceIdentity.end());
+
+ // The final size
+ data[0] = data.size();
+
+ Stream stream{data};
+ Callout callout{stream};
+
+ EXPECT_EQ(callout.flattenedSize(), data.size());
+
+ // Spot check the 2 substructures
+ EXPECT_TRUE(callout.fruIdentity());
+ EXPECT_EQ(callout.fruIdentity()->getSN(), "123456789ABC");
+
+ EXPECT_TRUE(callout.pceIdentity());
+ EXPECT_EQ(callout.pceIdentity()->enclosureName(), "PCENAME12");
+
+ // Not present
+ EXPECT_FALSE(callout.mru());
+
+ // Now flatten
+ std::vector<uint8_t> newData;
+ Stream newStream{newData};
+
+ callout.flatten(newStream);
+ EXPECT_EQ(data, newData);
+}
+
+TEST(CalloutTest, TestNoLocationCode)
+{
+ std::vector<uint8_t> data{
+ 0xFF, 0x2B, 'H', 0x00 // size, flags, priority, LC length
+ };
+
+ auto fruIdentity = srcDataFactory(TestSRCType::fruIdentityStructure);
+ data.insert(data.end(), fruIdentity.begin(), fruIdentity.end());
+
+ // The final size
+ data[0] = data.size();
+
+ Stream stream{data};
+ Callout callout{stream};
+
+ EXPECT_TRUE(callout.locationCode().empty());
+}
OpenPOWER on IntegriCloud