summaryrefslogtreecommitdiffstats
path: root/extensions/openpower-pels
diff options
context:
space:
mode:
authorMatt Spinler <spinler@us.ibm.com>2019-12-02 15:50:12 -0600
committerMatt Spinler <spinler@us.ibm.com>2019-12-09 13:47:57 -0600
commitc63e2e82ba5b11114dfa1b5aa9ba8fb5bfa25382 (patch)
tree5ad5a1d154245fa215e7512fa06a6bb1d9483efd /extensions/openpower-pels
parentcad9c2bd9e8ffb6e6db297d8d71b9733aae96e1e (diff)
downloadphosphor-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/openpower-pels')
-rw-r--r--extensions/openpower-pels/extended_user_header.cpp179
-rw-r--r--extensions/openpower-pels/extended_user_header.hpp254
-rw-r--r--extensions/openpower-pels/openpower-pels.mk1
-rw-r--r--extensions/openpower-pels/pel.cpp5
-rw-r--r--extensions/openpower-pels/section_factory.cpp4
-rw-r--r--extensions/openpower-pels/src.hpp34
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
OpenPOWER on IntegriCloud