diff options
author | Matt Spinler <spinler@us.ibm.com> | 2019-10-08 13:42:05 -0500 |
---|---|---|
committer | Matt Spinler <spinler@us.ibm.com> | 2019-10-22 14:09:56 +0000 |
commit | a906c940ef1ebb78d8ae876ec90777aa447a3a76 (patch) | |
tree | 2d2ea00d5bd4ca16a13844413e2caee9b84bfcab | |
parent | 6852d7222d9268d2b205672d6296424c0581c830 (diff) | |
download | phosphor-logging-a906c940ef1ebb78d8ae876ec90777aa447a3a76.tar.gz phosphor-logging-a906c940ef1ebb78d8ae876ec90777aa447a3a76.zip |
PEL: FRU identity SRC substructure
This substructure is part of the callout subsection in the SRC
section of a PEL, and contains information about the FRU (Field
Replaceable Unit) being called out.
This includes:
* The specific type of FRU (see the flags field definitions)
* The FRU part number
* The FRU CCIN value (CCIN = a keyword in VPD).
* The FRU serial number
Instead of just calling out a FRU, this structure can instead be used to
call out a maintenance procedure, which is a string that is used as
a key into the service documentation that maps to a procedure to fix
the problem.
This commit only adds support for creating an object from a flattened PEL,
such as one that comes down from the host. A future commit will handle
creating it from scratch for BMC errors.
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
Change-Id: Ic2b9489abea48084116bf2f450bd293c2d655979
-rw-r--r-- | extensions/openpower-pels/fru_identity.cpp | 98 | ||||
-rw-r--r-- | extensions/openpower-pels/fru_identity.hpp | 222 | ||||
-rw-r--r-- | extensions/openpower-pels/openpower-pels.mk | 1 | ||||
-rw-r--r-- | test/openpower-pels/Makefile.include | 9 | ||||
-rw-r--r-- | test/openpower-pels/fru_identity_test.cpp | 74 |
5 files changed, 404 insertions, 0 deletions
diff --git a/extensions/openpower-pels/fru_identity.cpp b/extensions/openpower-pels/fru_identity.cpp new file mode 100644 index 0000000..d62bf6a --- /dev/null +++ b/extensions/openpower-pels/fru_identity.cpp @@ -0,0 +1,98 @@ +#include "fru_identity.hpp" + +namespace openpower +{ +namespace pels +{ +namespace src +{ + +FRUIdentity::FRUIdentity(Stream& pel) +{ + pel >> _type >> _size >> _flags; + + if (_flags & (pnSupplied | maintProcSupplied)) + { + pel.read(_pnOrProcedureID.data(), _pnOrProcedureID.size()); + } + + if (_flags & ccinSupplied) + { + pel.read(_ccin.data(), _ccin.size()); + } + + if (_flags & snSupplied) + { + pel.read(_sn.data(), _sn.size()); + } +} + +std::optional<std::string> FRUIdentity::getPN() const +{ + if (hasPN()) + { + // NULL terminated + std::string pn{_pnOrProcedureID.data()}; + return pn; + } + + return std::nullopt; +} + +std::optional<std::string> FRUIdentity::getMaintProc() const +{ + if (hasMP()) + { + // NULL terminated + std::string mp{_pnOrProcedureID.data()}; + return mp; + } + + return std::nullopt; +} + +std::optional<std::string> FRUIdentity::getCCIN() const +{ + if (hasCCIN()) + { + std::string ccin{_ccin.begin(), _ccin.begin() + _ccin.size()}; + return ccin; + } + + return std::nullopt; +} + +std::optional<std::string> FRUIdentity::getSN() const +{ + if (hasSN()) + { + std::string sn{_sn.begin(), _sn.begin() + _sn.size()}; + return sn; + } + + return std::nullopt; +} + +void FRUIdentity::flatten(Stream& pel) +{ + pel << _type << _size << _flags; + + if (hasPN() || hasMP()) + { + pel.write(_pnOrProcedureID.data(), _pnOrProcedureID.size()); + } + + if (hasCCIN()) + { + pel.write(_ccin.data(), _ccin.size()); + } + + if (hasSN()) + { + pel.write(_sn.data(), _sn.size()); + } +} + +} // namespace src +} // namespace pels +} // namespace openpower diff --git a/extensions/openpower-pels/fru_identity.hpp b/extensions/openpower-pels/fru_identity.hpp new file mode 100644 index 0000000..76142a8 --- /dev/null +++ b/extensions/openpower-pels/fru_identity.hpp @@ -0,0 +1,222 @@ +#pragma once + +#include "stream.hpp" + +#include <optional> + +namespace openpower +{ +namespace pels +{ +namespace src +{ + +/** + * @class FRUIdentity + * + * This represents the FRU Identity substructure in the + * callout subsection of the SRC PEL section. + * + * It provides information about the FRU being called out, + * such as serial number and part number. A maintenance + * procedure name may be used instead of the part number, + * and this would be indicated in the flags field. + */ +class FRUIdentity +{ + public: + /** + * @brief The failing component type + * + * Upper nibble of the flags byte + */ + enum FailingComponentType + { + hardwareFRU = 0x10, + codeFRU = 0x20, + configError = 0x30, + maintenanceProc = 0x40, + externalFRU = 0x90, + externalCodeFRU = 0xA0, + toolFRU = 0xB0, + symbolicFRU = 0xC0, + symbolicFRUTrustedLocCode = 0xE0 + }; + + /** + * @brief The lower nibble of the flags byte + */ + enum Flags + { + pnSupplied = 0x08, + ccinSupplied = 0x04, + maintProcSupplied = 0x02, + snSupplied = 0x01 + }; + + FRUIdentity() = delete; + ~FRUIdentity() = default; + FRUIdentity(const FRUIdentity&) = default; + FRUIdentity& operator=(const FRUIdentity&) = default; + FRUIdentity(FRUIdentity&&) = default; + FRUIdentity& operator=(FRUIdentity&&) = default; + + /** + * @brief Constructor + * + * Fills in this class's data fields from the stream. + * + * @param[in] pel - the PEL data stream + */ + explicit FRUIdentity(Stream& pel); + + /** + * @brief Flatten the object into the stream + * + * @param[in] stream - The stream to write to + */ + void flatten(Stream& pel); + + /** + * @brief Returns the size of this structure when flattened into a PEL + * + * @return size_t - The size of the section + */ + size_t flattenedSize() const + { + return _size; + } + + /** + * @brief The failing component type for this FRU callout. + * + * @return FailingComponentType + */ + FailingComponentType failingComponentType() const + { + return static_cast<FailingComponentType>(_flags & 0xF0); + } + + /** + * @brief Returns the part number, if supplied + * + * @return std::optional<std::string> + */ + std::optional<std::string> getPN() const; + + /** + * @brief Returns the maintenance procedure, if supplied + * + * @return std::optional<std::string> + */ + std::optional<std::string> getMaintProc() const; + + /** + * @brief Returns the CCIN, if supplied + * + * @return std::optional<std::string> + */ + std::optional<std::string> getCCIN() const; + + /** + * @brief Returns the serial number, if supplied + * + * @return std::optional<std::string> + */ + std::optional<std::string> getSN() const; + + /** + * @brief The type identifier value of this structure. + */ + static const uint16_t substructureType = 0x4944; // "ID" + + private: + /** + * @brief If the part number is contained in this structure. + * + * It takes the place of the maintenance procedure ID. + * + * @return bool + */ + bool hasPN() const + { + return _flags & pnSupplied; + } + + /** + * @brief If the CCIN is contained in this structure. + * + * @return bool + */ + bool hasCCIN() const + { + return _flags & ccinSupplied; + } + + /** + * @brief If a maintenance procedure is contained in this structure. + * + * It takes the place of the part number. + * + * @return bool + */ + bool hasMP() const + { + return _flags & maintProcSupplied; + } + + /** + * @brief If the serial number is contained in this structure. + * + * @return bool + */ + bool hasSN() const + { + return _flags & snSupplied; + } + + /** + * @brief The callout substructure type field. Will be "ID". + */ + uint16_t _type; + + /** + * @brief The size of this callout structure. + * + * Always a multiple of 4. + */ + uint8_t _size; + + /** + * @brief The flags byte of this substructure. + * + * See the FailingComponentType and Flags enums + */ + uint8_t _flags; + + /** + * @brief The part number OR maintenance procedure ID, + * depending on what the flags field specifies. + * + * A NULL terminated ASCII string. + */ + std::array<char, 8> _pnOrProcedureID; + + /** + * @brief The CCIN VPD keyword + * + * Four ASCII characters, not NULL terminated. + */ + std::array<char, 4> _ccin; + + /** + * @brief The serial number + * + * Twelve ASCII characters, not NULL terminated. + */ + std::array<char, 12> _sn; +}; + +} // namespace src +} // namespace pels +} // namespace openpower diff --git a/extensions/openpower-pels/openpower-pels.mk b/extensions/openpower-pels/openpower-pels.mk index 07d7522..9a4f4fc 100644 --- a/extensions/openpower-pels/openpower-pels.mk +++ b/extensions/openpower-pels/openpower-pels.mk @@ -4,6 +4,7 @@ phosphor_log_manager_SOURCES += \ extensions/openpower-pels/data_interface.cpp \ extensions/openpower-pels/entry_points.cpp \ extensions/openpower-pels/failing_mtms.cpp \ + extensions/openpower-pels/fru_identity.cpp \ extensions/openpower-pels/generic.cpp \ extensions/openpower-pels/log_id.cpp \ extensions/openpower-pels/manager.cpp \ diff --git a/test/openpower-pels/Makefile.include b/test/openpower-pels/Makefile.include index d9a6375..f824030 100644 --- a/test/openpower-pels/Makefile.include +++ b/test/openpower-pels/Makefile.include @@ -5,6 +5,7 @@ check_PROGRAMS += \ ascii_string_test \ bcd_time_test \ failing_mtms_test \ + fru_identity_test \ generic_section_test \ log_id_test \ mtms_test \ @@ -199,3 +200,11 @@ ascii_string_test_LDADD = \ $(test_ldadd) \ $(top_builddir)/extensions/openpower-pels/ascii_string.o ascii_string_test_LDFLAGS = $(test_ldflags) + +fru_identity_test_SOURCES = %reldir%/fru_identity_test.cpp +fru_identity_test_CPPFLAGS = $(test_cppflags) +fru_identity_test_CXXFLAGS = $(test_cxxflags) +fru_identity_test_LDADD = \ + $(test_ldadd) \ + $(top_builddir)/extensions/openpower-pels/fru_identity.o +fru_identity_test_LDFLAGS = $(test_ldflags) diff --git a/test/openpower-pels/fru_identity_test.cpp b/test/openpower-pels/fru_identity_test.cpp new file mode 100644 index 0000000..e985b80 --- /dev/null +++ b/test/openpower-pels/fru_identity_test.cpp @@ -0,0 +1,74 @@ +#include "extensions/openpower-pels/fru_identity.hpp" + +#include <gtest/gtest.h> + +using namespace openpower::pels; +using namespace openpower::pels::src; + +// Unflatten a FRUIdentity that is a HW FRU callout +TEST(FRUIdentityTest, TestHardwareFRU) +{ + // Has PN, SN, CCIN + std::vector<uint8_t> data{'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'}; + + Stream stream{data}; + + FRUIdentity fru{stream}; + + EXPECT_EQ(fru.failingComponentType(), FRUIdentity::hardwareFRU); + EXPECT_EQ(fru.flattenedSize(), data.size()); + + EXPECT_EQ(fru.getPN().value(), "1234567"); + EXPECT_EQ(fru.getCCIN().value(), "AAAA"); + EXPECT_EQ(fru.getSN().value(), "123456789ABC"); + EXPECT_FALSE(fru.getMaintProc()); + + // Flatten + std::vector<uint8_t> newData; + Stream newStream{newData}; + fru.flatten(newStream); + EXPECT_EQ(data, newData); +} + +// Unflatten a FRUIdentity that is a Maintenance Procedure callout +TEST(FRUIdentityTest, TestMaintProcedure) +{ + // Only contains the maintenance procedure + std::vector<uint8_t> data{ + 0x49, 0x44, 0x0C, 0x42, // type, size, flags + '1', '2', '3', '4', '5', '6', '7', 0x00 // Procedure + }; + + Stream stream{data}; + + FRUIdentity fru{stream}; + + EXPECT_EQ(fru.failingComponentType(), FRUIdentity::maintenanceProc); + EXPECT_EQ(fru.flattenedSize(), data.size()); + + EXPECT_EQ(fru.getMaintProc().value(), "1234567"); + EXPECT_FALSE(fru.getPN()); + EXPECT_FALSE(fru.getCCIN()); + EXPECT_FALSE(fru.getSN()); + + // Flatten + std::vector<uint8_t> newData; + Stream newStream{newData}; + fru.flatten(newStream); + EXPECT_EQ(data, newData); +} + +// Try to unflatten garbage data +TEST(FRUIdentityTest, BadDataTest) +{ + std::vector<uint8_t> data{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF}; + + Stream stream{data}; + + EXPECT_THROW(FRUIdentity fru{stream}, std::out_of_range); +} |