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
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