PEL: MRU callout SRC substructure

This substructure is part of the callout subsection in the SRC section
of a PEL, and contains information about Manufacturing Replaceable Units
(MRUs).  MRUs are components on a parent FRU (Field Replaceable Unit)
that may be able to be replaced in a manufacturing environment, hence
the name.

This substructure includes a list of <priority, MRU ID> pairs, where the
priority is the same priority value type as used elsewhere in the SRC
section ('H', 'M', 'L', etc), and the MRU ID is a 4B ID that development
will tell manufacturing the meanings of.

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: I6352e1a3cb84db0516902786faca4c387afef411
diff --git a/extensions/openpower-pels/mru.cpp b/extensions/openpower-pels/mru.cpp
new file mode 100644
index 0000000..89e0cfe
--- /dev/null
+++ b/extensions/openpower-pels/mru.cpp
@@ -0,0 +1,53 @@
+#include "mru.hpp"
+
+#include <phosphor-logging/log.hpp>
+
+namespace openpower
+{
+namespace pels
+{
+namespace src
+{
+
+using namespace phosphor::logging;
+
+MRU::MRU(Stream& pel)
+{
+    pel >> _type >> _size >> _flags >> _reserved4B;
+
+    size_t numMRUs = _flags & 0xF;
+
+    for (size_t i = 0; i < numMRUs; i++)
+    {
+        MRUCallout mru;
+        pel >> mru.priority;
+        pel >> mru.id;
+        _mrus.push_back(std::move(mru));
+    }
+
+    size_t actualSize = sizeof(_type) + sizeof(_size) + sizeof(_flags) +
+                        sizeof(_reserved4B) +
+                        (sizeof(MRUCallout) * _mrus.size());
+    if (_size != actualSize)
+    {
+        log<level::WARNING>("MRU callout section in PEL has listed size that "
+                            "doesn't match actual size",
+                            entry("SUBSTRUCTURE_SIZE=%lu", _size),
+                            entry("NUM_MRUS=%lu", _mrus.size()),
+                            entry("ACTUAL_SIZE=%lu", actualSize));
+    }
+}
+
+void MRU::flatten(Stream& pel)
+{
+    pel << _type << _size << _flags << _reserved4B;
+
+    for (auto& mru : _mrus)
+    {
+        pel << mru.priority;
+        pel << mru.id;
+    }
+}
+} // namespace src
+} // namespace pels
+} // namespace openpower
diff --git a/extensions/openpower-pels/mru.hpp b/extensions/openpower-pels/mru.hpp
new file mode 100644
index 0000000..8ad3e3c
--- /dev/null
+++ b/extensions/openpower-pels/mru.hpp
@@ -0,0 +1,122 @@
+#pragma once
+
+#include "stream.hpp"
+
+namespace openpower
+{
+namespace pels
+{
+namespace src
+{
+
+/**
+ * @class MRU
+ *
+ * This represents the MRU (Manufacturing Replaceable Unit)
+ * substructure in the callout subsection of the SRC PEL section.
+ *
+ * Manufacturing replaceable units have a finer granularity than
+ * a field replaceable unit, such as a chip on a card, and are
+ * intended to be used during manufacturing.
+ *
+ * This substructure can contain up to 128 MRU callouts, each
+ * containing a MRU ID and a callout priority value.
+ */
+class MRU
+{
+  public:
+    /**
+     * @brief A single MRU callout, which contains a priority
+     *        and a MRU ID.
+     *
+     * The priority value is the same priority type character
+     * value as in the parent callout structure. For alignment
+     * purposes it is a 4 byte field, though only the LSB contains
+     * the priority value.
+     */
+    struct MRUCallout
+    {
+        uint32_t priority;
+        uint32_t id;
+    };
+
+    MRU() = delete;
+    ~MRU() = default;
+    MRU(const MRU&) = default;
+    MRU& operator=(const MRU&) = default;
+    MRU(MRU&&) = default;
+    MRU& operator=(MRU&&) = default;
+
+    /**
+     * @brief Constructor
+     *
+     * Fills in this class's data fields from the stream.
+     *
+     * @param[in] pel - the PEL data stream
+     */
+    explicit MRU(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 Returns the contained MRU callouts.
+     *
+     * @return const std::vector<MRUCallout>& - The MRUs
+     */
+    const std::vector<MRUCallout>& mrus() const
+    {
+        return _mrus;
+    }
+
+    /**
+     * @brief The type identifier value of this structure.
+     */
+    static const uint16_t substructureType = 0x4D52; // "MR"
+
+  private:
+    /**
+     * @brief The callout substructure type field. Will be 'MR'.
+     */
+    uint16_t _type;
+
+    /**
+     * @brief The size of this callout structure.
+     */
+    uint8_t _size;
+
+    /**
+     * @brief The flags byte of this substructure.
+     *
+     * 0x0Y: Y = number of MRU callouts
+     */
+    uint8_t _flags;
+
+    /**
+     * @brief Reserved 4 bytes
+     */
+    uint32_t _reserved4B;
+
+    /*
+     * @brief The MRU callouts
+     */
+    std::vector<MRUCallout> _mrus;
+};
+
+} // namespace src
+} // namespace pels
+} // namespace openpower
diff --git a/extensions/openpower-pels/openpower-pels.mk b/extensions/openpower-pels/openpower-pels.mk
index 550530c..fdc998d 100644
--- a/extensions/openpower-pels/openpower-pels.mk
+++ b/extensions/openpower-pels/openpower-pels.mk
@@ -8,6 +8,7 @@
 	extensions/openpower-pels/generic.cpp \
 	extensions/openpower-pels/log_id.cpp \
 	extensions/openpower-pels/manager.cpp \
+	extensions/openpower-pels/mru.cpp \
 	extensions/openpower-pels/mtms.cpp \
 	extensions/openpower-pels/paths.cpp \
 	extensions/openpower-pels/pce_identity.cpp \