PEL: Add Callout object constructors
Add constructors to the PEL Callout object to create a FRU callout,
either a hardware FRU with PN/SN/CCIN, or a maintenance procedure.
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
Change-Id: I015370fbfa4e5522eada316d12c10b130e1de4be
diff --git a/extensions/openpower-pels/callout.cpp b/extensions/openpower-pels/callout.cpp
index 95c6408..905c212 100644
--- a/extensions/openpower-pels/callout.cpp
+++ b/extensions/openpower-pels/callout.cpp
@@ -26,6 +26,8 @@
using namespace phosphor::logging;
+constexpr size_t locationCodeMaxSize = 80;
+
Callout::Callout(Stream& pel)
{
pel >> _size >> _flags >> _priority >> _locationCodeSize;
@@ -76,7 +78,67 @@
}
}
-size_t Callout::flattenedSize()
+Callout::Callout(CalloutPriority priority, const std::string& locationCode,
+ const std::string& partNumber, const std::string& ccin,
+ const std::string& serialNumber)
+{
+ _flags = calloutType | fruIdentIncluded;
+
+ _priority = static_cast<uint8_t>(priority);
+
+ setLocationCode(locationCode);
+
+ _fruIdentity =
+ std::make_unique<FRUIdentity>(partNumber, ccin, serialNumber);
+
+ _size = flattenedSize();
+}
+
+Callout::Callout(CalloutPriority priority, MaintProcedure procedure)
+{
+ _flags = calloutType | fruIdentIncluded;
+
+ _priority = static_cast<uint8_t>(priority);
+
+ _locationCodeSize = 0;
+
+ _fruIdentity = std::make_unique<FRUIdentity>(procedure);
+
+ _size = flattenedSize();
+}
+
+void Callout::setLocationCode(const std::string& locationCode)
+{
+ if (locationCode.empty())
+ {
+ _locationCodeSize = 0;
+ return;
+ }
+
+ std::copy(locationCode.begin(), locationCode.end(),
+ std::back_inserter(_locationCode));
+
+ if (_locationCode.size() < locationCodeMaxSize)
+ {
+ // Add a NULL, and then pad to a 4B boundary
+ _locationCode.push_back('\0');
+
+ while (_locationCode.size() % 4)
+ {
+ _locationCode.push_back('\0');
+ }
+ }
+ else
+ {
+ // Too big - truncate it and ensure it ends in a NULL.
+ _locationCode.resize(locationCodeMaxSize);
+ _locationCode.back() = '\0';
+ }
+
+ _locationCodeSize = _locationCode.size();
+}
+
+size_t Callout::flattenedSize() const
{
size_t size = sizeof(_size) + sizeof(_flags) + sizeof(_priority) +
sizeof(_locationCodeSize) + _locationCodeSize;
diff --git a/extensions/openpower-pels/callout.hpp b/extensions/openpower-pels/callout.hpp
index caf3aec..c8bb0a3 100644
--- a/extensions/openpower-pels/callout.hpp
+++ b/extensions/openpower-pels/callout.hpp
@@ -3,6 +3,7 @@
#include "fru_identity.hpp"
#include "mru.hpp"
#include "pce_identity.hpp"
+#include "pel_types.hpp"
#include "stream.hpp"
namespace openpower
@@ -34,6 +35,19 @@
class Callout
{
public:
+ /**
+ * @brief Which callout substructures are included.
+ */
+ enum calloutFlags
+ {
+ calloutType = 0b0010'0000,
+ fruIdentIncluded = 0b0000'1000,
+ mruIncluded = 0b0000'0100
+
+ // Leaving out the various PCE identity ones since
+ // we don't use them.
+ };
+
Callout() = delete;
~Callout() = default;
Callout(const Callout&) = delete;
@@ -51,11 +65,38 @@
explicit Callout(Stream& pel);
/**
+ * @brief Constructor
+ *
+ * Creates the objects with a FRUIdentity substructure that calls
+ * out a normal hardware FRU.
+ *
+ * @param[in] priority - The priority of the callout
+ * @param[in] locationCode - The location code of the callout
+ * @param[in] partNumber - The part number of the callout
+ * @param[in] ccin - The CCIN of the callout
+ * @param[in] serialNumber - The serial number of the callout
+ */
+ Callout(CalloutPriority priority, const std::string& locationCode,
+ const std::string& partNumber, const std::string& ccin,
+ const std::string& serialNumber);
+
+ /**
+ * @brief Constructor
+ *
+ * Creates the objects with a FRUIdentity substructure that calls
+ * out maintenance procedure.
+ *
+ * @param[in] priority - The priority of the callout
+ * @param[in] procedure - The maintenance procedure
+ */
+ Callout(CalloutPriority priority, MaintProcedure procedure);
+
+ /**
* @brief Returns the size of this object when flattened into a PEL
*
* @return size_t - The size of the section
*/
- size_t flattenedSize();
+ size_t flattenedSize() const;
/**
* @brief Flatten the object into the stream
@@ -101,6 +142,16 @@
}
/**
+ * @brief Returns the location code size
+ *
+ * @return size_t - The size, including the terminating null.
+ */
+ size_t locationCodeSize() const
+ {
+ return _locationCodeSize;
+ }
+
+ /**
* @brief Returns the FRU identity substructure
*
* @return const std::unique_ptr<FRUIdentity>&
@@ -123,7 +174,7 @@
/**
* @brief Returns the MRU identity substructure
*
- * @return const std::unique_ptr<FRUIdentity>&
+ * @return const std::unique_ptr<MRU>&
*/
const std::unique_ptr<MRU>& mru() const
{
@@ -132,6 +183,13 @@
private:
/**
+ * @brief Sets the location code field
+ *
+ * @param[in] locationCode - The location code string
+ */
+ void setLocationCode(const std::string& locationCode);
+
+ /**
* @brief The size of this structure in the PEL
*/
uint8_t _size;
@@ -152,7 +210,7 @@
* Includes the NULL termination, and must be a
* multiple of 4 (padded with zeros)
*/
- uint8_t _locationCodeSize;
+ uint8_t _locationCodeSize = 0;
/**
* @brief NULL terminated location code
diff --git a/test/openpower-pels/src_callout_test.cpp b/test/openpower-pels/src_callout_test.cpp
index 3fd2a5c..36e9e38 100644
--- a/test/openpower-pels/src_callout_test.cpp
+++ b/test/openpower-pels/src_callout_test.cpp
@@ -159,3 +159,162 @@
EXPECT_TRUE(callout.locationCode().empty());
}
+
+// Create a callout object by passing in the hardware fields to add
+TEST(CalloutTest, TestHardwareCallout)
+{
+ constexpr size_t fruIdentitySize = 28;
+
+ {
+ Callout callout{CalloutPriority::high, "U99-42.5-P1-C2-E1", "1234567",
+ "ABCD", "123456789ABC"};
+
+ // size/flags/pri/locsize fields +
+ // rounded up location code length +
+ // FRUIdentity size
+ size_t size = 4 + 20 + fruIdentitySize;
+
+ EXPECT_EQ(callout.flags(),
+ Callout::calloutType | Callout::fruIdentIncluded);
+
+ EXPECT_EQ(callout.flattenedSize(), size);
+ EXPECT_EQ(callout.priority(), 'H');
+ EXPECT_EQ(callout.locationCode(), "U99-42.5-P1-C2-E1");
+ EXPECT_EQ(callout.locationCodeSize(), 20);
+
+ auto& fru = callout.fruIdentity();
+ EXPECT_EQ(fru->getPN().value(), "1234567");
+ EXPECT_EQ(fru->getCCIN().value(), "ABCD");
+ EXPECT_EQ(fru->getSN().value(), "123456789ABC");
+ }
+
+ {
+ // A 3B location code, plus null = 4
+ Callout callout{CalloutPriority::high, "123", "1234567", "ABCD",
+ "123456789ABC"};
+
+ size_t size = 4 + 4 + fruIdentitySize;
+ EXPECT_EQ(callout.locationCodeSize(), 4);
+ EXPECT_EQ(callout.flattenedSize(), size);
+ EXPECT_EQ(callout.locationCode(), "123");
+ }
+
+ {
+ // A 4B location code, plus null = 5, then pad to 8
+ Callout callout{CalloutPriority::high, "1234", "1234567", "ABCD",
+ "123456789ABC"};
+
+ size_t size = 4 + 8 + fruIdentitySize;
+ EXPECT_EQ(callout.locationCodeSize(), 8);
+ EXPECT_EQ(callout.flattenedSize(), size);
+ EXPECT_EQ(callout.locationCode(), "1234");
+ }
+
+ {
+ // A truncated location code (80 is max size, including null)
+ std::string locCode(81, 'L');
+ Callout callout{CalloutPriority::high, locCode, "1234567", "ABCD",
+ "123456789ABC"};
+
+ size_t size = 4 + 80 + fruIdentitySize;
+ EXPECT_EQ(callout.locationCodeSize(), 80);
+ EXPECT_EQ(callout.flattenedSize(), size);
+
+ // take off 1 to get to 80, and another for the null
+ locCode = locCode.substr(0, locCode.size() - 2);
+ EXPECT_EQ(callout.locationCode(), locCode);
+ }
+
+ {
+ // A truncated location code by 1 because of the null
+ std::string locCode(80, 'L');
+ Callout callout{CalloutPriority::high, locCode, "1234567", "ABCD",
+ "123456789ABC"};
+
+ size_t size = 4 + 80 + fruIdentitySize;
+ EXPECT_EQ(callout.locationCodeSize(), 80);
+ EXPECT_EQ(callout.flattenedSize(), size);
+
+ locCode.pop_back();
+ EXPECT_EQ(callout.locationCode(), locCode);
+ }
+
+ {
+ // Max size location code
+ std::string locCode(79, 'L');
+ Callout callout{CalloutPriority::low, locCode, "1234567", "ABCD",
+ "123456789ABC"};
+
+ size_t size = 4 + 80 + fruIdentitySize;
+ EXPECT_EQ(callout.locationCodeSize(), 80);
+ EXPECT_EQ(callout.flattenedSize(), size);
+
+ EXPECT_EQ(callout.locationCode(), locCode);
+
+ // How about we flatten/unflatten this last one
+ std::vector<uint8_t> data;
+ Stream stream{data};
+
+ callout.flatten(stream);
+
+ {
+ Stream newStream{data};
+ Callout newCallout{newStream};
+
+ EXPECT_EQ(newCallout.flags(),
+ Callout::calloutType | Callout::fruIdentIncluded);
+
+ EXPECT_EQ(newCallout.flattenedSize(), callout.flattenedSize());
+ EXPECT_EQ(newCallout.priority(), callout.priority());
+ EXPECT_EQ(newCallout.locationCode(), callout.locationCode());
+ EXPECT_EQ(newCallout.locationCodeSize(),
+ callout.locationCodeSize());
+
+ auto& fru = newCallout.fruIdentity();
+ EXPECT_EQ(fru->getPN().value(), "1234567");
+ EXPECT_EQ(fru->getCCIN().value(), "ABCD");
+ EXPECT_EQ(fru->getSN().value(), "123456789ABC");
+ }
+ }
+}
+
+// Create a callout object by passing in the maintenance procedure to add.
+TEST(CalloutTest, TestProcedureCallout)
+{
+ Callout callout{CalloutPriority::medium, MaintProcedure::noVPDforFRU};
+
+ // size/flags/pri/locsize fields + FRUIdentity size
+ // No location code.
+ size_t size = 4 + 12;
+
+ EXPECT_EQ(callout.flags(),
+ Callout::calloutType | Callout::fruIdentIncluded);
+
+ EXPECT_EQ(callout.flattenedSize(), size);
+ EXPECT_EQ(callout.priority(), 'M');
+ EXPECT_EQ(callout.locationCode(), "");
+ EXPECT_EQ(callout.locationCodeSize(), 0);
+
+ auto& fru = callout.fruIdentity();
+ EXPECT_EQ(fru->getMaintProc().value(), "BMCSP01");
+
+ // flatten/unflatten
+ std::vector<uint8_t> data;
+ Stream stream{data};
+
+ callout.flatten(stream);
+
+ Stream newStream{data};
+ Callout newCallout{newStream};
+
+ EXPECT_EQ(newCallout.flags(),
+ Callout::calloutType | Callout::fruIdentIncluded);
+
+ EXPECT_EQ(newCallout.flattenedSize(), callout.flattenedSize());
+ EXPECT_EQ(newCallout.priority(), callout.priority());
+ EXPECT_EQ(newCallout.locationCode(), callout.locationCode());
+ EXPECT_EQ(newCallout.locationCodeSize(), callout.locationCodeSize());
+
+ auto& newFRU = newCallout.fruIdentity();
+ EXPECT_EQ(newFRU->getMaintProc().value(), fru->getMaintProc().value());
+}