summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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