diff options
-rw-r--r-- | extensions/openpower-pels/callout.cpp | 102 | ||||
-rw-r--r-- | extensions/openpower-pels/callout.hpp | 173 | ||||
-rw-r--r-- | extensions/openpower-pels/openpower-pels.mk | 1 | ||||
-rw-r--r-- | test/openpower-pels/Makefile.include | 15 | ||||
-rw-r--r-- | test/openpower-pels/pel_utils.cpp | 43 | ||||
-rw-r--r-- | test/openpower-pels/pel_utils.hpp | 21 | ||||
-rw-r--r-- | test/openpower-pels/src_callout_test.cpp | 146 |
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()); +} |