Add register capture data FFDC to PEL
Signed-off-by: Zane Shelley <zshelle@us.ibm.com>
Change-Id: Ie6e0dcb0d700ad5d87b0e834bc9880cbe927f28a
diff --git a/analyzer/create_pel.cpp b/analyzer/create_pel.cpp
index 2c384c4..b77d048 100644
--- a/analyzer/create_pel.cpp
+++ b/analyzer/create_pel.cpp
@@ -1,5 +1,6 @@
#include <unistd.h>
+#include <analyzer/util.hpp>
#include <hei_main.hpp>
#include <phosphor-logging/elog.hpp>
#include <sdbusplus/bus.hpp>
@@ -22,8 +23,8 @@
enum FfdcSubType_t : uint8_t
{
- FFDC_SIGNATURES = 0x01,
- FFDC_CAPTURE_DATA = 0x02,
+ FFDC_SIGNATURES = 0x01,
+ FFDC_REGISTER_DUMP = 0x02,
// For the callout section, the value of '0xCA' is required per the
// phosphor-logging openpower-pel extention spec.
@@ -128,6 +129,80 @@
//------------------------------------------------------------------------------
+void __captureRegisterDump(const libhei::IsolationData& i_isoData,
+ std::vector<util::FFDCFile>& io_userDataFiles)
+{
+ // Create a new entry for this user data section regardless if there are any
+ // registers in the dump.
+ io_userDataFiles.emplace_back(util::FFDCFormat::Custom, FFDC_REGISTER_DUMP,
+ FFDC_VERSION1);
+
+ // Create a streamer for easy writing to the FFDC file.
+ auto path = io_userDataFiles.back().getPath();
+ util::BinFileWriter stream{path};
+
+ // The first 4 bytes in the FFDC contains the number of chips with register
+ // data. Then the data for each chip will follow.
+
+ auto dump = i_isoData.getRegisterDump();
+
+ uint32_t numChips = dump.size();
+ stream << numChips;
+
+ for (const auto& entry : dump)
+ {
+ auto chip = entry.first;
+ auto regList = entry.second;
+
+ // Each chip will have the following information:
+ // 4 byte chip model/EC
+ // 2 byte chip position
+ // 4 byte number of registers
+ // Then the data for each register will follow.
+
+ uint32_t chipType = chip.getType();
+ uint16_t chipPos = util::pdbg::getChipPos(chip);
+ uint32_t numRegs = regList.size();
+ stream << chipType << chipPos << numRegs;
+
+ for (const auto& reg : regList)
+ {
+ // Each register will have the following information:
+ // 3 byte register ID
+ // 1 byte register instance
+ // 1 byte data size
+ // * byte data buffer (* depends on value of data size)
+
+ libhei::RegisterId_t regId = reg.regId; // 3 byte
+ libhei::Instance_t regInst = reg.regInst; // 1 byte
+
+ auto tmp = libhei::BitString::getMinBytes(reg.data->getBitLen());
+ if (255 < tmp)
+ {
+ trace::inf("Register data execeeded 255 and was truncated: "
+ "regId=0x%06x regInst=%u",
+ regId, regInst);
+ tmp = 255;
+ }
+ uint8_t dataSize = tmp;
+
+ stream << regId << regInst << dataSize;
+
+ stream.write(reg.data->getBufAddr(), dataSize);
+ }
+ }
+
+ // If the stream failed for any reason, remove the FFDC file.
+ if (!stream.good())
+ {
+ trace::err("Unable to write register dump FFDC file: %s",
+ path.string().c_str());
+ io_userDataFiles.pop_back();
+ }
+}
+
+//------------------------------------------------------------------------------
+
std::string __getMessageRegistry(bool i_isCheckstop)
{
// For now, there are only two choices:
@@ -183,6 +258,9 @@
// Capture the complete signature list.
__captureSignatureList(i_isoData, userDataFiles);
+ // Capture the complete signature list.
+ __captureRegisterDump(i_isoData, userDataFiles);
+
// Now, that all of the user data files have been created, transform the
// data into the proper format for the PEL.
std::vector<util::FFDCTuple> userData;
diff --git a/analyzer/util.hpp b/analyzer/util.hpp
new file mode 100644
index 0000000..de7c404
--- /dev/null
+++ b/analyzer/util.hpp
@@ -0,0 +1,37 @@
+#pragma once
+
+/**
+ * @brief Specially utilities that are specific to the analyzer (typically
+ * stuff that involves libhei).
+ */
+
+#include <hei_main.hpp>
+#include <util/bin_stream.hpp>
+
+namespace util
+{
+
+/** @brief Extracts big-endian data to host RegisterId_t. */
+template <>
+inline BinFileReader& BinFileReader::operator>>(libhei::RegisterId_t& r)
+{
+ // A register ID is only 3 bytes, but there isn't a 3-byte integer type.
+ // So extract 3 bytes to a uint32_t and drop the unused byte.
+ uint32_t tmp = 0;
+ read(&tmp, 3);
+ r = static_cast<libhei::RegisterId_t>(be32toh(tmp) >> 8);
+ return *this;
+}
+
+/** @brief Inserts host RegisterId_t to big-endian data. */
+template <>
+inline BinFileWriter& BinFileWriter::operator<<(libhei::RegisterId_t r)
+{
+ // A register ID is only 3 bytes, but there isn't a 3-byte integer type.
+ // So extract 3 bytes to a uint32_t and drop the unused byte.
+ uint32_t tmp = htobe32(static_cast<uint32_t>(r) << 8);
+ write(&tmp, 3);
+ return *this;
+}
+
+} // namespace util