diff options
author | Matt Spinler <spinler@us.ibm.com> | 2019-12-02 15:50:12 -0600 |
---|---|---|
committer | Matt Spinler <spinler@us.ibm.com> | 2019-12-09 13:47:57 -0600 |
commit | c63e2e82ba5b11114dfa1b5aa9ba8fb5bfa25382 (patch) | |
tree | 5ad5a1d154245fa215e7512fa06a6bb1d9483efd /extensions | |
parent | cad9c2bd9e8ffb6e6db297d8d71b9733aae96e1e (diff) | |
download | phosphor-logging-c63e2e82ba5b11114dfa1b5aa9ba8fb5bfa25382.tar.gz phosphor-logging-c63e2e82ba5b11114dfa1b5aa9ba8fb5bfa25382.zip |
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 <spinler@us.ibm.com>
Change-Id: I223041f85965195fccf69542dbe86ce856073b36
Diffstat (limited to 'extensions')
-rw-r--r-- | extensions/openpower-pels/extended_user_header.cpp | 179 | ||||
-rw-r--r-- | extensions/openpower-pels/extended_user_header.hpp | 254 | ||||
-rw-r--r-- | extensions/openpower-pels/openpower-pels.mk | 1 | ||||
-rw-r--r-- | extensions/openpower-pels/pel.cpp | 5 | ||||
-rw-r--r-- | extensions/openpower-pels/section_factory.cpp | 4 | ||||
-rw-r--r-- | extensions/openpower-pels/src.hpp | 34 |
6 files changed, 460 insertions, 17 deletions
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 <phosphor-logging/log.hpp> + +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<level::ERR>("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<uint16_t>(SectionID::extendedUserHeader); + _header.version = extendedUserHeaderVersion; + _header.subType = 0; + _header.componentID = static_cast<uint16_t>(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<uint16_t>(SectionID::extendedUserHeader)) + { + log<level::ERR>("Invalid failing Extended User Header section ID", + entry("ID=0x%X", header().id)); + failed = true; + } + + if (header().version != extendedUserHeaderVersion) + { + log<level::ERR>("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<size_t> 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<uint8_t, firmwareVersionSize> _serverFWVersion; + + /** + * @brief The subsystem firmware version + * + * NULL terminated. + * + * On PELs created on the BMC, this will be the BMC code version. + */ + std::array<uint8_t, firmwareVersionSize> _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<uint8_t> _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<UserHeader>(entry, severity); auto src = std::make_unique<SRC>(entry, additionalData); + + auto euh = std::make_unique<ExtendedUserHeader>(dataIface, entry, *src); + _optionalSections.push_back(std::move(src)); + _optionalSections.push_back(std::move(euh)); auto mtms = std::make_unique<FailingMTMS>(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<Section> create(Stream& pelData) case static_cast<uint16_t>(SectionID::secondarySRC): section = std::make_unique<SRC>(pelData); break; + case static_cast<uint16_t>(SectionID::extendedUserHeader): + section = std::make_unique<ExtendedUserHeader>(pelData); + break; default: // A generic object, but at least an object. section = std::make_unique<Generic>(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 @@ -222,23 +239,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. * * This is only used for BMC generated SRCs, where words 6 - 9 are the |