summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatt Spinler <spinler@us.ibm.com>2019-10-08 13:42:05 -0500
committerMatt Spinler <spinler@us.ibm.com>2019-10-22 14:09:56 +0000
commita906c940ef1ebb78d8ae876ec90777aa447a3a76 (patch)
tree2d2ea00d5bd4ca16a13844413e2caee9b84bfcab
parent6852d7222d9268d2b205672d6296424c0581c830 (diff)
downloadphosphor-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.cpp98
-rw-r--r--extensions/openpower-pels/fru_identity.hpp222
-rw-r--r--extensions/openpower-pels/openpower-pels.mk1
-rw-r--r--test/openpower-pels/Makefile.include9
-rw-r--r--test/openpower-pels/fru_identity_test.cpp74
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);
+}
OpenPOWER on IntegriCloud