From c63e2e82ba5b11114dfa1b5aa9ba8fb5bfa25382 Mon Sep 17 00:00:00 2001 From: Matt Spinler Date: Mon, 2 Dec 2019 15:50:12 -0600 Subject: PEL: Add ExtendedUserHeader section class This is a required PEL section. The section contains: * The machine type/model/SN * The server firmware version * The BMC firmware version * The 'Event Common Reference Time' (not used yet) * The symptom ID (a unique event signature) Signed-off-by: Matt Spinler Change-Id: I223041f85965195fccf69542dbe86ce856073b36 --- extensions/openpower-pels/extended_user_header.cpp | 179 ++++++++++++ extensions/openpower-pels/extended_user_header.hpp | 254 +++++++++++++++++ extensions/openpower-pels/openpower-pels.mk | 1 + extensions/openpower-pels/pel.cpp | 5 + extensions/openpower-pels/section_factory.cpp | 4 + extensions/openpower-pels/src.hpp | 34 +-- test/openpower-pels/Makefile.include | 24 +- test/openpower-pels/extended_user_header_test.cpp | 301 +++++++++++++++++++++ test/openpower-pels/pel_utils.cpp | 32 ++- 9 files changed, 813 insertions(+), 21 deletions(-) create mode 100644 extensions/openpower-pels/extended_user_header.cpp create mode 100644 extensions/openpower-pels/extended_user_header.hpp create mode 100644 test/openpower-pels/extended_user_header_test.cpp diff --git a/extensions/openpower-pels/extended_user_header.cpp b/extensions/openpower-pels/extended_user_header.cpp new file mode 100644 index 0000000..c5c1938 --- /dev/null +++ b/extensions/openpower-pels/extended_user_header.cpp @@ -0,0 +1,179 @@ +/** + * Copyright © 2019 IBM Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "extended_user_header.hpp" + +#include "pel_types.hpp" + +#include + +namespace openpower +{ +namespace pels +{ + +using namespace phosphor::logging; +const size_t defaultSymptomIDWord = 3; +const size_t symptomIDMaxSize = 80; + +ExtendedUserHeader::ExtendedUserHeader(Stream& pel) +{ + try + { + unflatten(pel); + validate(); + } + catch (const std::exception& e) + { + log("Cannot unflatten extended user header", + entry("ERROR=%s", e.what())); + _valid = false; + } +} + +ExtendedUserHeader::ExtendedUserHeader(const DataInterfaceBase& dataIface, + const message::Entry& regEntry, + const SRC& src) : + _mtms(dataIface.getMachineTypeModel(), dataIface.getMachineSerialNumber()) +{ + _header.id = static_cast(SectionID::extendedUserHeader); + _header.version = extendedUserHeaderVersion; + _header.subType = 0; + _header.componentID = static_cast(ComponentID::phosphorLogging); + + memset(_serverFWVersion.data(), 0, _serverFWVersion.size()); + auto version = dataIface.getServerFWVersion(); + + // The last byte must always be the NULL terminator + for (size_t i = 0; i < version.size() && i < firmwareVersionSize - 1; i++) + { + _serverFWVersion[i] = version[i]; + } + + memset(_subsystemFWVersion.data(), 0, _subsystemFWVersion.size()); + version = dataIface.getBMCFWVersion(); + + // The last byte must always be the NULL terminator + for (size_t i = 0; i < version.size() && i < firmwareVersionSize - 1; i++) + { + _subsystemFWVersion[i] = version[i]; + } + + createSymptomID(regEntry, src); + + _header.size = flattenedSize(); + _valid = true; +} + +void ExtendedUserHeader::flatten(Stream& pel) const +{ + pel << _header << _mtms; + pel.write(_serverFWVersion.data(), _serverFWVersion.size()); + pel.write(_subsystemFWVersion.data(), _subsystemFWVersion.size()); + pel << _reserved4B << _refTime << _reserved1B1 << _reserved1B2 + << _reserved1B3 << _symptomIDSize << _symptomID; +} + +void ExtendedUserHeader::unflatten(Stream& pel) +{ + pel >> _header >> _mtms; + pel.read(_serverFWVersion.data(), _serverFWVersion.size()); + pel.read(_subsystemFWVersion.data(), _subsystemFWVersion.size()); + pel >> _reserved4B >> _refTime >> _reserved1B1 >> _reserved1B2 >> + _reserved1B3 >> _symptomIDSize; + + _symptomID.resize(_symptomIDSize); + pel >> _symptomID; +} + +void ExtendedUserHeader::validate() +{ + bool failed = false; + + if (header().id != static_cast(SectionID::extendedUserHeader)) + { + log("Invalid failing Extended User Header section ID", + entry("ID=0x%X", header().id)); + failed = true; + } + + if (header().version != extendedUserHeaderVersion) + { + log("Invalid Extended User Header version", + entry("VERSION=0x%X", header().version)); + failed = true; + } + + _valid = (failed) ? false : true; +} + +void ExtendedUserHeader::createSymptomID(const message::Entry& regEntry, + const SRC& src) +{ + // Contains the first 8 characters of the ASCII string plus additional + // words from the SRC, separated by underscores. The message registry + // says which words to use, though that's optional and if not present + // then use a default word. + std::vector idWords; + + if (regEntry.src.symptomID) + { + idWords = regEntry.src.symptomID.value(); + } + else + { + idWords.push_back(defaultSymptomIDWord); + } + + auto symptomID = src.asciiString().substr(0, 8); + + const auto& hexWords = src.hexwordData(); + + for (auto wordNum : idWords) + { + symptomID.push_back('_'); + + // Get the hexword array index for this SRC word + auto index = src.getWordIndexFromWordNum(wordNum); + + // Convert to ASCII + char word[20]; + sprintf(word, "%08X", hexWords[index]); + symptomID += word; + } + + std::copy(symptomID.begin(), symptomID.end(), + std::back_inserter(_symptomID)); + + // Max total size is 80, including the upcoming NULL + if (_symptomID.size() > (symptomIDMaxSize - 1)) + { + _symptomID.resize(symptomIDMaxSize - 1); + } + + // NULL terminated + _symptomID.push_back(0); + + // PAD with NULLs to a 4 byte boundary + while ((_symptomID.size() % 4) != 0) + { + _symptomID.push_back(0); + } + + _symptomIDSize = _symptomID.size(); +} + +} // namespace pels +} // namespace openpower diff --git a/extensions/openpower-pels/extended_user_header.hpp b/extensions/openpower-pels/extended_user_header.hpp new file mode 100644 index 0000000..422b3f5 --- /dev/null +++ b/extensions/openpower-pels/extended_user_header.hpp @@ -0,0 +1,254 @@ +#pragma once + +#include "bcd_time.hpp" +#include "data_interface.hpp" +#include "elog_entry.hpp" +#include "mtms.hpp" +#include "registry.hpp" +#include "section.hpp" +#include "src.hpp" +#include "stream.hpp" + +namespace openpower +{ +namespace pels +{ + +constexpr uint8_t extendedUserHeaderVersion = 0x01; +constexpr size_t firmwareVersionSize = 16; + +/** + * @class ExtendedUserHeader + * + * This represents the Extended User Header section in a PEL. It is a required + * section. It contains code versions, an MTMS subsection, and a string called + * a symptom ID. + * + * The Section base class handles the section header structure that every + * PEL section has at offset zero. + */ +class ExtendedUserHeader : public Section +{ + public: + ExtendedUserHeader() = delete; + ~ExtendedUserHeader() = default; + ExtendedUserHeader(const ExtendedUserHeader&) = default; + ExtendedUserHeader& operator=(const ExtendedUserHeader&) = default; + ExtendedUserHeader(ExtendedUserHeader&&) = default; + ExtendedUserHeader& operator=(ExtendedUserHeader&&) = default; + + /** + * @brief Constructor + * + * Fills in this class's data fields from the stream. + * + * @param[in] pel - the PEL data stream + */ + explicit ExtendedUserHeader(Stream& pel); + + /** + * @brief Constructor + * + * @param[in] dataIface - The DataInterface object + * @param[in] regEntry - The message registry entry for this event + * @param[in] src - The SRC section object for this event + */ + ExtendedUserHeader(const DataInterfaceBase& dataIface, + const message::Entry& regEntry, const SRC& src); + + /** + * @brief Flatten the section into the stream + * + * @param[in] stream - The stream to write to + */ + void flatten(Stream& stream) const override; + + /** + * @brief Returns the size of this section when flattened into a PEL + * + * @return size_t - the size of the section + */ + size_t flattenedSize() + { + return Section::flattenedSize() + _mtms.flattenedSize() + + _serverFWVersion.size() + _subsystemFWVersion.size() + + sizeof(_reserved4B) + sizeof(_refTime) + sizeof(_reserved1B1) + + sizeof(_reserved1B2) + sizeof(_reserved1B3) + + sizeof(_symptomIDSize) + _symptomIDSize; + } + + /** + * @brief Returns the server firmware version + * + * @return std::string - The version + */ + std::string serverFWVersion() const + { + std::string version; + for (size_t i = 0; + i < _serverFWVersion.size() && _serverFWVersion[i] != '\0'; i++) + { + version.push_back(_serverFWVersion[i]); + } + return version; + } + + /** + * @brief Returns the subsystem firmware version + * + * @return std::string - The version + */ + std::string subsystemFWVersion() const + { + std::string version; + for (size_t i = 0; + i < _subsystemFWVersion.size() && _subsystemFWVersion[i] != '\0'; + i++) + { + version.push_back(_subsystemFWVersion[i]); + } + return version; + } + + /** + * @brief Returns the machine type+model + * + * @return std::string - The MTM + */ + std::string machineTypeModel() const + { + return _mtms.machineTypeAndModel(); + } + + /** + * @brief Returns the machine serial number + * + * @return std::string - The serial number + */ + std::string machineSerialNumber() const + { + return _mtms.machineSerialNumber(); + } + + /** + * @brief Returns the Event Common Reference Time field + * + * @return BCDTime - The time value + */ + const BCDTime& refTime() const + { + return _refTime; + } + + /** + * @brief Returns the symptom ID + * + * @return std::string - The symptom ID + */ + std::string symptomID() const + { + std::string symptom; + for (size_t i = 0; i < _symptomID.size() && _symptomID[i] != '\0'; i++) + { + symptom.push_back(_symptomID[i]); + } + return symptom; + } + + private: + /** + * @brief Fills in the object from the stream data + * + * @param[in] stream - The stream to read from + */ + void unflatten(Stream& stream); + + /** + * @brief Validates the section contents + * + * Updates _valid (in Section) with the results. + */ + void validate() override; + + /** + * @brief Builds the symptom ID + * + * Uses the message registry to say which SRC fields to add + * to the SRC's ASCII string to make the ID, and uses a smart + * default if not specified in the registry. + * + * @param[in] regEntry - The message registry entry for this event + * @param[in] src - The SRC section object for this event + */ + void createSymptomID(const message::Entry& regEntry, const SRC& src); + + /** + * @brief The structure that holds the machine TM and SN fields. + */ + MTMS _mtms; + + /** + * @brief The server firmware version + * + * NULL terminated. + * + * The release version of the full firmware image. + */ + std::array _serverFWVersion; + + /** + * @brief The subsystem firmware version + * + * NULL terminated. + * + * On PELs created on the BMC, this will be the BMC code version. + */ + std::array _subsystemFWVersion; + + /** + * @brief Reserved + */ + uint32_t _reserved4B = 0; + + /** + * @brief Event Common Reference Time + * + * This is not used by PELs created on the BMC. + */ + BCDTime _refTime; + + /** + * @brief Reserved + */ + uint8_t _reserved1B1 = 0; + + /** + * @brief Reserved + */ + uint8_t _reserved1B2 = 0; + + /** + * @brief Reserved + */ + uint8_t _reserved1B3 = 0; + + /** + * @brief The size of the symptom ID field + */ + uint8_t _symptomIDSize; + + /** + * @brief The symptom ID field + * + * Describes a unique event signature for the log. + * Required for serviceable events, otherwise optional. + * When present, must start with the first 8 characters + * of the ASCII string field from the SRC. + * + * NULL terminated. + */ + std::vector _symptomID; +}; + +} // namespace pels +} // namespace openpower diff --git a/extensions/openpower-pels/openpower-pels.mk b/extensions/openpower-pels/openpower-pels.mk index 35b1b29..5bce70d 100644 --- a/extensions/openpower-pels/openpower-pels.mk +++ b/extensions/openpower-pels/openpower-pels.mk @@ -14,6 +14,7 @@ libpel_la_SOURCES = \ extensions/openpower-pels/callout.cpp \ extensions/openpower-pels/callouts.cpp \ extensions/openpower-pels/data_interface.cpp \ + extensions/openpower-pels/extended_user_header.cpp \ extensions/openpower-pels/failing_mtms.cpp \ extensions/openpower-pels/fru_identity.cpp \ extensions/openpower-pels/generic.cpp \ diff --git a/extensions/openpower-pels/pel.cpp b/extensions/openpower-pels/pel.cpp index e006541..022ac05 100644 --- a/extensions/openpower-pels/pel.cpp +++ b/extensions/openpower-pels/pel.cpp @@ -16,6 +16,7 @@ #include "pel.hpp" #include "bcd_time.hpp" +#include "extended_user_header.hpp" #include "failing_mtms.hpp" #include "hexdump.hpp" #include "log_id.hpp" @@ -46,7 +47,11 @@ PEL::PEL(const message::Entry& entry, uint32_t obmcLogID, uint64_t timestamp, _uh = std::make_unique(entry, severity); auto src = std::make_unique(entry, additionalData); + + auto euh = std::make_unique(dataIface, entry, *src); + _optionalSections.push_back(std::move(src)); + _optionalSections.push_back(std::move(euh)); auto mtms = std::make_unique(dataIface); _optionalSections.push_back(std::move(mtms)); diff --git a/extensions/openpower-pels/section_factory.cpp b/extensions/openpower-pels/section_factory.cpp index 8a52a02..43a7d70 100644 --- a/extensions/openpower-pels/section_factory.cpp +++ b/extensions/openpower-pels/section_factory.cpp @@ -15,6 +15,7 @@ */ #include "section_factory.hpp" +#include "extended_user_header.hpp" #include "failing_mtms.hpp" #include "generic.hpp" #include "pel_types.hpp" @@ -62,6 +63,9 @@ std::unique_ptr
create(Stream& pelData) case static_cast(SectionID::secondarySRC): section = std::make_unique(pelData); break; + case static_cast(SectionID::extendedUserHeader): + section = std::make_unique(pelData); + break; default: // A generic object, but at least an object. section = std::make_unique(pelData); diff --git a/extensions/openpower-pels/src.hpp b/extensions/openpower-pels/src.hpp index e6f7029..dada63a 100644 --- a/extensions/openpower-pels/src.hpp +++ b/extensions/openpower-pels/src.hpp @@ -195,6 +195,23 @@ class SRC : public Section return _flags & powerFaultEvent; } + /** + * @brief Get the _hexData[] index to use based on the corresponding + * SRC word number. + * + * Converts the specification nomenclature to this data structure. + * See the _hexData documentation below for more information. + * + * @param[in] wordNum - The SRC word number, as defined by the spec. + * + * @return size_t The corresponding index into _hexData. + */ + inline size_t getWordIndexFromWordNum(size_t wordNum) const + { + assert(wordNum >= 2 && wordNum <= 9); + return wordNum - 2; + } + private: /** * @brief Fills in the user defined hex words from the @@ -221,23 +238,6 @@ class SRC : public Section */ void unflatten(Stream& stream); - /** - * @brief Get the _hexData[] index to use based on the corresponding - * SRC word number. - * - * Converts the specification nomenclature to this data structure. - * See the _hexData documentation below for more information. - * - * @param[in] wordNum - The SRC word number, as defined by the spec. - * - * @return size_t The corresponding index into _hexData. - */ - inline size_t getWordIndexFromWordNum(size_t wordNum) const - { - assert(wordNum >= 2 && wordNum <= 9); - return wordNum - 2; - } - /** * @brief Says if the word number is in the range of user defined words. * diff --git a/test/openpower-pels/Makefile.include b/test/openpower-pels/Makefile.include index 7f3a589..dfa4156 100644 --- a/test/openpower-pels/Makefile.include +++ b/test/openpower-pels/Makefile.include @@ -4,6 +4,7 @@ check_PROGRAMS += \ additional_data_test \ ascii_string_test \ bcd_time_test \ + extended_user_header_test \ failing_mtms_test \ fru_identity_test \ generic_section_test \ @@ -33,10 +34,11 @@ pel_objects = \ $(top_builddir)/extensions/openpower-pels/bcd_time.o \ $(top_builddir)/extensions/openpower-pels/callout.o \ $(top_builddir)/extensions/openpower-pels/callouts.o \ + $(top_builddir)/extensions/openpower-pels/extended_user_header.o \ $(top_builddir)/extensions/openpower-pels/failing_mtms.o \ $(top_builddir)/extensions/openpower-pels/fru_identity.o \ $(top_builddir)/extensions/openpower-pels/generic.o \ - $(top_builddir)/extensions/openpower-pels/hexdump.o \ + $(top_builddir)/extensions/openpower-pels/hexdump.o \ $(top_builddir)/extensions/openpower-pels/log_id.o \ $(top_builddir)/extensions/openpower-pels/mtms.o \ $(top_builddir)/extensions/openpower-pels/mru.o \ @@ -287,6 +289,26 @@ src_test_LDADD = \ $(top_builddir)/extensions/openpower-pels/src.o src_test_LDFLAGS = $(test_ldflags) +extended_user_header_test_SOURCES = \ + %reldir%/extended_user_header_test.cpp \ + %reldir%/pel_utils.cpp +extended_user_header_test_CPPFLAGS = $(test_cppflags) +extended_user_header_test_CXXFLAGS = $(test_cxxflags) +extended_user_header_test_LDADD = \ + $(test_ldadd) \ + $(top_builddir)/extensions/openpower-pels/ascii_string.o \ + $(top_builddir)/extensions/openpower-pels/bcd_time.o \ + $(top_builddir)/extensions/openpower-pels/callout.o \ + $(top_builddir)/extensions/openpower-pels/callouts.o \ + $(top_builddir)/extensions/openpower-pels/data_interface.o \ + $(top_builddir)/extensions/openpower-pels/extended_user_header.o \ + $(top_builddir)/extensions/openpower-pels/fru_identity.o \ + $(top_builddir)/extensions/openpower-pels/mru.o \ + $(top_builddir)/extensions/openpower-pels/mtms.o \ + $(top_builddir)/extensions/openpower-pels/pce_identity.o \ + $(top_builddir)/extensions/openpower-pels/src.o +extended_user_header_test_LDFLAGS = $(test_ldflags) + pel_rules_test_SOURCES = %reldir%/pel_rules_test.cpp pel_rules_test_CPPFLAGS = $(test_cppflags) pel_rules_test_CXXFLAGS = $(test_cxxflags) diff --git a/test/openpower-pels/extended_user_header_test.cpp b/test/openpower-pels/extended_user_header_test.cpp new file mode 100644 index 0000000..5cb3e9f --- /dev/null +++ b/test/openpower-pels/extended_user_header_test.cpp @@ -0,0 +1,301 @@ +/** + * Copyright © 2019 IBM Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "extensions/openpower-pels/extended_user_header.hpp" +#include "mocks.hpp" +#include "pel_utils.hpp" + +#include + +using namespace openpower::pels; +using ::testing::Return; + +const std::vector sectionData{ + // section header + 'E', 'H', 0x00, 0x60, // ID and Size + 0x01, 0x00, // version, subtype + 0x03, 0x04, // comp ID + + // MTMS + 'T', 'T', 'T', 'T', '-', 'M', 'M', 'M', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', + + // Server FW version + 'S', 'E', 'R', 'V', 'E', 'R', '_', 'V', 'E', 'R', 'S', 'I', 'O', 'N', '\0', + '\0', + + // Subsystem FW Version + 'B', 'M', 'C', '_', 'V', 'E', 'R', 'S', 'I', 'O', 'N', '\0', '\0', '\0', + '\0', '\0', + + // Reserved + 0x00, 0x00, 0x00, 0x00, + + // Reference time + 0x20, 0x25, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, + + // Reserved + 0x00, 0x00, 0x00, + + // SymptomID length + 20, + + // SymptomID + 'B', 'D', '8', 'D', '4', '2', '0', '0', '_', '1', '2', '3', '4', '5', '6', + '7', '8', '\0', '\0', '\0'}; + +// The section size without the symptom ID +const size_t baseSectionSize = 76; + +TEST(ExtUserHeaderTest, StreamConstructorTest) +{ + auto data = sectionData; + Stream stream{data}; + ExtendedUserHeader euh{stream}; + + EXPECT_EQ(euh.valid(), true); + EXPECT_EQ(euh.header().id, 0x4548); // EH + EXPECT_EQ(euh.header().size, sectionData.size()); + EXPECT_EQ(euh.header().version, 0x01); + EXPECT_EQ(euh.header().subType, 0x00); + EXPECT_EQ(euh.header().componentID, 0x0304); + + EXPECT_EQ(euh.flattenedSize(), sectionData.size()); + EXPECT_EQ(euh.machineTypeModel(), "TTTT-MMM"); + EXPECT_EQ(euh.machineSerialNumber(), "123456789ABC"); + EXPECT_EQ(euh.serverFWVersion(), "SERVER_VERSION"); + EXPECT_EQ(euh.subsystemFWVersion(), "BMC_VERSION"); + EXPECT_EQ(euh.symptomID(), "BD8D4200_12345678"); + + BCDTime time{0x20, 0x25, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60}; + EXPECT_EQ(time, euh.refTime()); + + // Flatten it and make sure nothing changes + std::vector newData; + Stream newStream{newData}; + + euh.flatten(newStream); + EXPECT_EQ(sectionData, newData); +} + +// Same as above, with with symptom ID empty +TEST(ExtUserHeaderTest, StreamConstructorNoIDTest) +{ + auto data = sectionData; + data.resize(baseSectionSize); + data[3] = baseSectionSize; // The size in the header + data.back() = 0; // Symptom ID length + + Stream stream{data}; + ExtendedUserHeader euh{stream}; + + EXPECT_EQ(euh.valid(), true); + EXPECT_EQ(euh.header().id, 0x4548); // EH + EXPECT_EQ(euh.header().size, baseSectionSize); + EXPECT_EQ(euh.header().version, 0x01); + EXPECT_EQ(euh.header().subType, 0x00); + EXPECT_EQ(euh.header().componentID, 0x0304); + + EXPECT_EQ(euh.flattenedSize(), baseSectionSize); + EXPECT_EQ(euh.machineTypeModel(), "TTTT-MMM"); + EXPECT_EQ(euh.machineSerialNumber(), "123456789ABC"); + EXPECT_EQ(euh.serverFWVersion(), "SERVER_VERSION"); + EXPECT_EQ(euh.subsystemFWVersion(), "BMC_VERSION"); + EXPECT_EQ(euh.symptomID(), ""); + + BCDTime time{0x20, 0x25, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60}; + EXPECT_EQ(time, euh.refTime()); + + // Flatten it and make sure nothing changes + std::vector newData; + Stream newStream{newData}; + + euh.flatten(newStream); + EXPECT_EQ(data, newData); +} + +TEST(ExtUserHeaderTest, ConstructorTest) +{ + auto srcData = pelDataFactory(TestPELType::primarySRCSection); + Stream srcStream{srcData}; + SRC src{srcStream}; + + message::Entry entry; // Empty Symptom ID vector + + { + MockDataInterface dataIface; + + EXPECT_CALL(dataIface, getMachineTypeModel()) + .WillOnce(Return("AAAA-BBB")); + + EXPECT_CALL(dataIface, getMachineSerialNumber()) + .WillOnce(Return("123456789ABC")); + + EXPECT_CALL(dataIface, getServerFWVersion()) + .WillOnce(Return("SERVER_VERSION")); + + EXPECT_CALL(dataIface, getBMCFWVersion()) + .WillOnce(Return("BMC_VERSION")); + + ExtendedUserHeader euh{dataIface, entry, src}; + + EXPECT_EQ(euh.valid(), true); + EXPECT_EQ(euh.header().id, 0x4548); // EH + + // The symptom ID accounts for the extra 20 bytes + EXPECT_EQ(euh.header().size, baseSectionSize + 20); + EXPECT_EQ(euh.header().version, 0x01); + EXPECT_EQ(euh.header().subType, 0x00); + EXPECT_EQ(euh.header().componentID, 0x2000); + + EXPECT_EQ(euh.flattenedSize(), baseSectionSize + 20); + EXPECT_EQ(euh.machineTypeModel(), "AAAA-BBB"); + EXPECT_EQ(euh.machineSerialNumber(), "123456789ABC"); + EXPECT_EQ(euh.serverFWVersion(), "SERVER_VERSION"); + EXPECT_EQ(euh.subsystemFWVersion(), "BMC_VERSION"); + + // The default symptom ID is the ascii string + word 3 + EXPECT_EQ(euh.symptomID(), "BD8D5678_03030310"); + + BCDTime time; + EXPECT_EQ(time, euh.refTime()); + } + + { + MockDataInterface dataIface; + + // These 4 items are too long and will get truncated + // in the section. + EXPECT_CALL(dataIface, getMachineTypeModel()) + .WillOnce(Return("AAAA-BBBBBBBBBBB")); + + EXPECT_CALL(dataIface, getMachineSerialNumber()) + .WillOnce(Return("123456789ABC123456789")); + + EXPECT_CALL(dataIface, getServerFWVersion()) + .WillOnce(Return("SERVER_VERSION_WAY_TOO_LONG")); + + EXPECT_CALL(dataIface, getBMCFWVersion()) + .WillOnce(Return("BMC_VERSION_WAY_TOO_LONG")); + + // Use SRC words 3 through 9 + entry.src.symptomID = {3, 4, 5, 6, 7, 8, 9}; + ExtendedUserHeader euh{dataIface, entry, src}; + + EXPECT_EQ(euh.valid(), true); + EXPECT_EQ(euh.header().id, 0x4548); // EH + EXPECT_EQ(euh.header().size, baseSectionSize + 72); + EXPECT_EQ(euh.header().version, 0x01); + EXPECT_EQ(euh.header().subType, 0x00); + EXPECT_EQ(euh.header().componentID, 0x2000); + + EXPECT_EQ(euh.flattenedSize(), baseSectionSize + 72); + EXPECT_EQ(euh.machineTypeModel(), "AAAA-BBB"); + EXPECT_EQ(euh.machineSerialNumber(), "123456789ABC"); + EXPECT_EQ(euh.serverFWVersion(), "SERVER_VERSION_"); + EXPECT_EQ(euh.subsystemFWVersion(), "BMC_VERSION_WAY"); + + EXPECT_EQ(euh.symptomID(), "BD8D5678_03030310_04040404_05050505_" + "06060606_07070707_08080808_09090909"); + BCDTime time; + EXPECT_EQ(time, euh.refTime()); + } + + { + MockDataInterface dataIface; + + // Empty fields + EXPECT_CALL(dataIface, getMachineTypeModel()).WillOnce(Return("")); + + EXPECT_CALL(dataIface, getMachineSerialNumber()).WillOnce(Return("")); + + EXPECT_CALL(dataIface, getServerFWVersion()).WillOnce(Return("")); + + EXPECT_CALL(dataIface, getBMCFWVersion()).WillOnce(Return("")); + + entry.src.symptomID = {8, 9}; + ExtendedUserHeader euh{dataIface, entry, src}; + + EXPECT_EQ(euh.valid(), true); + EXPECT_EQ(euh.header().id, 0x4548); // EH + EXPECT_EQ(euh.header().size, baseSectionSize + 28); + EXPECT_EQ(euh.header().version, 0x01); + EXPECT_EQ(euh.header().subType, 0x00); + EXPECT_EQ(euh.header().componentID, 0x2000); + + EXPECT_EQ(euh.flattenedSize(), baseSectionSize + 28); + EXPECT_EQ(euh.machineTypeModel(), ""); + EXPECT_EQ(euh.machineSerialNumber(), ""); + EXPECT_EQ(euh.serverFWVersion(), ""); + EXPECT_EQ(euh.subsystemFWVersion(), ""); + + EXPECT_EQ(euh.symptomID(), "BD8D5678_08080808_09090909"); + + BCDTime time; + EXPECT_EQ(time, euh.refTime()); + } + + { + MockDataInterface dataIface; + + EXPECT_CALL(dataIface, getMachineTypeModel()) + .WillOnce(Return("AAAA-BBB")); + + EXPECT_CALL(dataIface, getMachineSerialNumber()) + .WillOnce(Return("123456789ABC")); + + EXPECT_CALL(dataIface, getServerFWVersion()) + .WillOnce(Return("SERVER_VERSION")); + + EXPECT_CALL(dataIface, getBMCFWVersion()) + .WillOnce(Return("BMC_VERSION")); + + // Way too long, will be truncated + entry.src.symptomID = {9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9}; + + ExtendedUserHeader euh{dataIface, entry, src}; + + EXPECT_EQ(euh.valid(), true); + EXPECT_EQ(euh.header().id, 0x4548); // EH + EXPECT_EQ(euh.header().size, baseSectionSize + 80); + EXPECT_EQ(euh.header().version, 0x01); + EXPECT_EQ(euh.header().subType, 0x00); + EXPECT_EQ(euh.header().componentID, 0x2000); + + EXPECT_EQ(euh.flattenedSize(), baseSectionSize + 80); + EXPECT_EQ(euh.machineTypeModel(), "AAAA-BBB"); + EXPECT_EQ(euh.machineSerialNumber(), "123456789ABC"); + EXPECT_EQ(euh.serverFWVersion(), "SERVER_VERSION"); + EXPECT_EQ(euh.subsystemFWVersion(), "BMC_VERSION"); + + EXPECT_EQ(euh.symptomID(), + "BD8D5678_09090909_09090909_09090909_09090909_09090909_" + "09090909_09090909_0909090"); + + BCDTime time; + EXPECT_EQ(time, euh.refTime()); + } +} + +TEST(ExtUserHeaderTest, BadDataTest) +{ + auto data = sectionData; + data.resize(20); + + Stream stream{data}; + ExtendedUserHeader euh{stream}; + + EXPECT_EQ(euh.valid(), false); +} diff --git a/test/openpower-pels/pel_utils.cpp b/test/openpower-pels/pel_utils.cpp index 5fdab54..18095a1 100644 --- a/test/openpower-pels/pel_utils.cpp +++ b/test/openpower-pels/pel_utils.cpp @@ -81,19 +81,43 @@ const std::vector srcSectionNoCallouts{ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}; -std::vector failingMTMSSection{ +const std::vector failingMTMSSection{ // Header 0x4D, 0x54, 0x00, 0x1C, 0x01, 0x00, 0x20, 0x00, 'T', 'T', 'T', 'T', '-', 'M', 'M', 'M', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C'}; -std::vector UserDataSection{ +const std::vector UserDataSection{ // Header 0x55, 0x44, 0x00, 0x10, 0x00, 0x00, 0x20, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; +const std::vector ExtUserHeaderSection{ + // Header + 'E', 'H', 0x00, 0x60, 0x01, 0x00, 0x03, 0x04, + + // MTMS + 'T', 'T', 'T', 'T', '-', 'M', 'M', 'M', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', + + // Server FW version + 'S', 'E', 'R', 'V', 'E', 'R', '_', 'V', 'E', 'R', 'S', 'I', 'O', 'N', '\0', + '\0', + + // Subsystem FW Version + 'B', 'M', 'C', '_', 'V', 'E', 'R', 'S', 'I', 'O', 'N', '\0', '\0', '\0', + '\0', '\0', + + 0x00, 0x00, 0x00, 0x00, // Reserved + 0x20, 0x25, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, // Ref time + 0x00, 0x00, 0x00, // Reserved + + // SymptomID length and symptom ID + 20, 'B', 'D', '8', 'D', '4', '2', '0', '0', '_', '1', '2', '3', '4', '5', + '6', '7', '8', '\0', '\0', '\0'}; + const std::vector srcFRUIdentityCallout{ 'I', 'D', 0x1C, 0x1D, // type, size, flags '1', '2', '3', '4', // PN @@ -140,7 +164,9 @@ std::vector pelDataFactory(TestPELType type) failingMTMSSection.end()); data.insert(data.end(), UserDataSection.begin(), UserDataSection.end()); - data.at(sectionCountOffset) = 5; + data.insert(data.end(), ExtUserHeaderSection.begin(), + ExtUserHeaderSection.end()); + data.at(sectionCountOffset) = 6; break; case TestPELType::privateHeaderSection: data.insert(data.end(), privateHeaderSection.begin(), -- cgit v1.2.1