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