summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHarisuddin Mohamed Isa <harisuddin@gmail.com>2020-01-15 20:05:33 +0800
committerHarisuddin Mohamed Isa <harisuddin@gmail.com>2020-02-03 11:56:44 +0800
commit0f717e1063154f0baec62f80e043d4d1c3317ce0 (patch)
tree4c2e91a3a7fe18bdd7456c7a77212c8501d15bfe
parent56e08263d6c3c7dccca02a7b3338cc0a37f4d716 (diff)
downloadphosphor-logging-0f717e1063154f0baec62f80e043d4d1c3317ce0.tar.gz
phosphor-logging-0f717e1063154f0baec62f80e043d4d1c3317ce0.zip
PEL: Print SRC section into JSON
For BMC created errors, look up the reason code in the message registry for error description and also meaning of data stored in hexwords 6-9 (if any). Added registry message field in peltool list output. "Primary SRC": { "Section Version": "1", "Sub-section type": "1", "Created by": "0x1000", "SRC Version": "0x02", "SRC Format": "0x55", "Power Control Net Fault": "False", "Error Details": { "Message": "PS 0x64 had a PGOOD Fault", "PS_NUM": "0x64" }, "Valid Word Count": "0x09", "Reference Code": "BD8D1001", "Hex Word 2": "00000055", "Hex Word 3": "00000010", "Hex Word 4": "00000000", "Hex Word 5": "00000000", "Hex Word 6": "00000064", "Hex Word 7": "00000000", "Hex Word 8": "00000000", "Hex Word 9": "00000000" } "Primary SRC": { "Section Version": "1", "Sub-section type": "0", "Created by": "0x4552", "SRC Version": "0x02", "SRC Format": "0x2008000", "Power Control Net Fault": "False", "Valid Word Count": "0x04", "Reference Code": "B2001020", "Hex Word 2": "02008000", "Hex Word 3": "00000000", "Hex Word 4": "00000012", "Callout Section": { "Callout Count": "1", "Callouts": [{ "FRU Type": "Symbolic FRU", "Priority": "Medium Priority", "Part Number": "NEXTLVL" }] } } Testing: Manually run peltool and verified out. All unit tests passed. Signed-off-by: Harisuddin Mohamed Isa <harisuddin@gmail.com> Change-Id: I124627ba785413ebda02305b7d9f95431922e714
-rw-r--r--extensions/openpower-pels/callout.hpp10
-rw-r--r--extensions/openpower-pels/json_utils.cpp2
-rw-r--r--extensions/openpower-pels/json_utils.hpp27
-rw-r--r--extensions/openpower-pels/manager.cpp3
-rw-r--r--extensions/openpower-pels/pel_values.cpp43
-rw-r--r--extensions/openpower-pels/pel_values.hpp15
-rw-r--r--extensions/openpower-pels/registry.cpp90
-rw-r--r--extensions/openpower-pels/registry.hpp62
-rw-r--r--extensions/openpower-pels/src.cpp258
-rw-r--r--extensions/openpower-pels/src.hpp41
-rw-r--r--extensions/openpower-pels/tools/peltool.cpp30
-rw-r--r--test/openpower-pels/Makefile.include8
-rw-r--r--test/openpower-pels/pel_manager_test.cpp5
-rw-r--r--test/openpower-pels/registry_test.cpp41
14 files changed, 580 insertions, 55 deletions
diff --git a/extensions/openpower-pels/callout.hpp b/extensions/openpower-pels/callout.hpp
index 76eb0f9..caf3aec 100644
--- a/extensions/openpower-pels/callout.hpp
+++ b/extensions/openpower-pels/callout.hpp
@@ -65,6 +65,16 @@ class Callout
void flatten(Stream& pel) const;
/**
+ * @brief Returns the flags field of a callout
+ *
+ * @return uint8_t - The flags
+ */
+ uint8_t flags() const
+ {
+ return _flags;
+ }
+
+ /**
* @brief Returns the priority field of a callout
*
* @return uint8_t - The priority
diff --git a/extensions/openpower-pels/json_utils.cpp b/extensions/openpower-pels/json_utils.cpp
index c6ea9f6..a25311c 100644
--- a/extensions/openpower-pels/json_utils.cpp
+++ b/extensions/openpower-pels/json_utils.cpp
@@ -139,7 +139,7 @@ char* dumpHex(const void* data, size_t size)
}
void jsonInsert(std::string& jsonStr, const std::string& fieldName,
- std::string& fieldValue, uint8_t indentCount)
+ std::string fieldValue, uint8_t indentCount)
{
const int8_t spacesToAppend =
colAlign - (indentCount * indentLevel) - fieldName.length() - 3;
diff --git a/extensions/openpower-pels/json_utils.hpp b/extensions/openpower-pels/json_utils.hpp
index 14c2ce4..f3fb767 100644
--- a/extensions/openpower-pels/json_utils.hpp
+++ b/extensions/openpower-pels/json_utils.hpp
@@ -39,7 +39,7 @@ char* dumpHex(const void* data, size_t size);
* @param[in] indentCount - Indent count for the line
*/
void jsonInsert(std::string& jsonStr, const std::string& fieldName,
- std::string& fieldValue, uint8_t indentCount);
+ std::string fieldValue, uint8_t indentCount);
/**
* @brief Inserts key-value array into a JSON string
@@ -51,5 +51,30 @@ void jsonInsert(std::string& jsonStr, const std::string& fieldName,
*/
void jsonInsertArray(std::string& jsonStr, const std::string& fieldName,
std::vector<std::string>& values, uint8_t indentCount);
+
+/**
+ * @brief Converts an integer to a formatted string
+ * @param[in] format - the format of output string
+ * @param[in] number - the integer to convert
+ * @return std::string - the formatted string
+ */
+template <typename T>
+std::string getNumberString(const char* format, T number)
+{
+ char* value = nullptr;
+ std::string numString;
+
+ static_assert(std::is_integral<T>::value, "Integral required.");
+
+ int len = asprintf(&value, format, number);
+ if (len)
+ {
+ numString = value;
+ }
+ free(value);
+
+ return numString;
+}
+
} // namespace pels
} // namespace openpower
diff --git a/extensions/openpower-pels/manager.cpp b/extensions/openpower-pels/manager.cpp
index a705e67..a00da37 100644
--- a/extensions/openpower-pels/manager.cpp
+++ b/extensions/openpower-pels/manager.cpp
@@ -31,6 +31,7 @@ namespace pels
using namespace phosphor::logging;
namespace fs = std::filesystem;
+namespace rg = openpower::pels::message;
namespace common_error = sdbusplus::xyz::openbmc_project::Common::Error;
@@ -130,7 +131,7 @@ void Manager::createPEL(const std::string& message, uint32_t obmcLogID,
const std::vector<std::string>& additionalData,
const std::vector<std::string>& associations)
{
- auto entry = _registry.lookup(message);
+ auto entry = _registry.lookup(message, rg::LookupType::name);
std::string msg;
if (entry)
diff --git a/extensions/openpower-pels/pel_values.cpp b/extensions/openpower-pels/pel_values.cpp
index cb00150..7e7f93b 100644
--- a/extensions/openpower-pels/pel_values.cpp
+++ b/extensions/openpower-pels/pel_values.cpp
@@ -206,12 +206,12 @@ const PELValues actionFlagsValues = {
* The possible values for the Callout Priority field in the SRC.
*/
const PELValues calloutPriorityValues = {
- {'H', "high", "Mandatory, replace all with this type as a unit"},
- {'M', "medium", "Medium Priority"},
- {'A', "medium_group_a", "Medium Priority A, replace these as a group"},
- {'B', "medium_group_b", "Medium Priority B, replace these as a group"},
- {'C', "medium_group_c", "Medium Priority C, replace these as a group"},
- {'L', "low", "Lowest priority replacement"}};
+ {0x48, "high", "Mandatory, replace all with this type as a unit"},
+ {0x4D, "medium", "Medium Priority"},
+ {0x41, "medium_group_a", "Medium Priority A, replace these as a group"},
+ {0x42, "medium_group_b", "Medium Priority B, replace these as a group"},
+ {0x43, "medium_group_c", "Medium Priority C, replace these as a group"},
+ {0x4C, "low", "Lowest priority replacement"}};
PELValues::const_iterator findByValue(uint32_t value, const PELValues& fields)
{
@@ -235,7 +235,6 @@ PELValues::const_iterator findByName(const std::string& name,
* @brief Map for section IDs
*/
const std::map<std::string, std::string> sectionTitles = {
-
{"PH", "Private Header"},
{"UH", "User Header"},
{"PS", "Primary SRC"},
@@ -244,7 +243,7 @@ const std::map<std::string, std::string> sectionTitles = {
{"MT", "Failing MTMS"},
{"DH", "Dump Location"},
{"SW", "Firmware Error"},
- {"LP", "Impacted Part"},
+ {"LP", "Impacted Partition"},
{"LR", "Logical Resource"},
{"HM", "HMC ID"},
{"EP", "EPOW"},
@@ -253,8 +252,32 @@ const std::map<std::string, std::string> sectionTitles = {
{"CH", "Call Home"},
{"UD", "User Data"},
{"EI", "Env Info"},
- {"ED", "Extended User Data"},
-};
+ {"ED", "Extended User Data"}};
+
+/**
+ * @brief Map for Procedure Descriptions
+ */
+const std::map<std::string, std::string> procedureDesc = {{"TODO", "TODO"}};
+
+/**
+ * @brief Map for Callout Failing Component Types
+ */
+const std::map<uint8_t, std::string> failingComponentType = {
+ {0x10, "Normal Hardware FRU"},
+ {0x20, "Code FRU"},
+ {0x30, "Configuration error, configuration procedure required"},
+ {0x40, "Maintenance Procedure Required"},
+ {0x90, "External FRU"},
+ {0xA0, "External Code FRU"},
+ {0xB0, "Tool FRU"},
+ {0xC0, "Symbolic FRU"},
+ {0xE0, "Symbolic FRU with trusted location code"}};
+
+/**
+ * @brief Map for Boolean value
+ */
+const std::map<bool, std::string> boolString = {{true, "True"},
+ {false, "False"}};
/**
* @brief Map for creator IDs
diff --git a/extensions/openpower-pels/pel_values.hpp b/extensions/openpower-pels/pel_values.hpp
index a52c8b3..2424c63 100644
--- a/extensions/openpower-pels/pel_values.hpp
+++ b/extensions/openpower-pels/pel_values.hpp
@@ -112,6 +112,21 @@ extern const std::map<std::string, std::string> creatorIDs;
*/
extern const std::map<TransmissionState, std::string> transmissionStates;
+/**
+ * @brief Map for Procedure Descriptions
+ */
+extern const std::map<std::string, std::string> procedureDesc;
+
+/**
+ * @brief Map for Callout Failing Component Types
+ */
+extern const std::map<uint8_t, std::string> failingComponentType;
+
+/**
+ * @brief Map for Boolean value
+ */
+extern const std::map<bool, std::string> boolString;
+
} // namespace pel_values
} // namespace pels
} // namespace openpower
diff --git a/extensions/openpower-pels/registry.cpp b/extensions/openpower-pels/registry.cpp
index d749ab9..3c4e016 100644
--- a/extensions/openpower-pels/registry.cpp
+++ b/extensions/openpower-pels/registry.cpp
@@ -264,37 +264,34 @@ uint16_t getComponentID(uint8_t srcType, uint16_t reasonCode,
} // namespace helper
-std::optional<Entry> Registry::lookup(const std::string& name)
+std::optional<Entry> Registry::lookup(const std::string& name, LookupType type,
+ bool toCache)
{
- // Look in /etc first in case someone put a test file there
- fs::path debugFile{fs::path{debugFilePath} / registryFileName};
- nlohmann::json registry;
- std::ifstream file;
-
- if (fs::exists(debugFile))
- {
- log<level::INFO>("Using debug PEL message registry");
- file.open(debugFile);
- }
- else
- {
- file.open(_registryFile);
- }
-
- try
+ std::optional<nlohmann::json> registryTmp;
+ auto& registryOpt = (_registry) ? _registry : registryTmp;
+ if (!registryOpt)
{
- registry = nlohmann::json::parse(file);
- }
- catch (std::exception& e)
- {
- log<level::ERR>("Error parsing message registry JSON",
- entry("JSON_ERROR=%s", e.what()));
- return std::nullopt;
+ registryOpt = readRegistry(_registryFile);
+ if (!registryOpt)
+ {
+ return std::nullopt;
+ }
+ else if (toCache)
+ {
+ // Save message registry in memory for peltool
+ _registry = std::move(registryTmp);
+ }
}
-
+ auto& reg = (_registry) ? _registry : registryTmp;
+ const auto& registry = reg.value();
// Find an entry with this name in the PEL array.
- auto e = std::find_if(registry["PELs"].begin(), registry["PELs"].end(),
- [&name](const auto& j) { return name == j["Name"]; });
+ auto e = std::find_if(
+ registry["PELs"].begin(), registry["PELs"].end(),
+ [&name, &type](const auto& j) {
+ return ((name == j["Name"] && type == LookupType::name) ||
+ (name == j["SRC"]["ReasonCode"] &&
+ type == LookupType::reasonCode));
+ });
if (e != registry["PELs"].end())
{
@@ -371,6 +368,14 @@ std::optional<Entry> Registry::lookup(const std::string& name)
entry.src.powerFault = src["PowerFault"];
}
+ auto& doc = (*e)["Documentation"];
+ entry.doc.message = doc["Message"];
+ entry.doc.description = doc["Description"];
+ if (doc.find("MessageArgSources") != doc.end())
+ {
+ entry.doc.messageArgSources = doc["MessageArgSources"];
+ }
+
return entry;
}
catch (std::exception& e)
@@ -383,6 +388,37 @@ std::optional<Entry> Registry::lookup(const std::string& name)
return std::nullopt;
}
+std::optional<nlohmann::json>
+ Registry::readRegistry(const std::filesystem::path& registryFile)
+{
+ // Look in /etc first in case someone put a test file there
+ fs::path debugFile{fs::path{debugFilePath} / registryFileName};
+ nlohmann::json registry;
+ std::ifstream file;
+
+ if (fs::exists(debugFile))
+ {
+ log<level::INFO>("Using debug PEL message registry");
+ file.open(debugFile);
+ }
+ else
+ {
+ file.open(registryFile);
+ }
+
+ try
+ {
+ registry = nlohmann::json::parse(file);
+ }
+ catch (std::exception& e)
+ {
+ log<level::ERR>("Error parsing message registry JSON",
+ entry("JSON_ERROR=%s", e.what()));
+ return std::nullopt;
+ }
+ return registry;
+}
+
} // namespace message
} // namespace pels
} // namespace openpower
diff --git a/extensions/openpower-pels/registry.hpp b/extensions/openpower-pels/registry.hpp
index d75c30a..0a75d8e 100644
--- a/extensions/openpower-pels/registry.hpp
+++ b/extensions/openpower-pels/registry.hpp
@@ -13,6 +13,39 @@ namespace message
{
constexpr auto registryFileName = "message_registry.json";
+enum class LookupType
+{
+ name = 0,
+ reasonCode = 1
+};
+
+/**
+ * @brief Represents the Documentation related fields in the message registry.
+ * It is part of the 'Entry' structure that will be filled in when
+ * an error is looked up in the registry.
+ *
+ * If a field is wrapped by std::optional, it means the field is
+ * optional in the JSON and higher level code knows how to handle it.
+ */
+struct DOC
+{
+ /**
+ * @brief Description of error
+ */
+ std::string description;
+
+ /**
+ * @brief Error message field
+ */
+ std::string message;
+
+ /**
+ * @brief An optional vector of SRC word 6-9 to use as the source of the
+ * numeric arguments that will be substituted into any placeholder
+ * in the Message field.
+ */
+ std::optional<std::vector<std::string>> messageArgSources;
+};
/**
* @brief Represents the SRC related fields in the message registry.
@@ -123,6 +156,11 @@ struct Entry
* The SRC related fields.
*/
SRC src;
+
+ /**
+ * The Documentation related fields.
+ */
+ DOC doc;
};
/**
@@ -155,24 +193,42 @@ class Registry
}
/**
- * @brief Find a registry entry based on its error name.
+ * @brief Find a registry entry based on its error name or reason code.
*
* This function does do some basic sanity checking on the JSON contents,
* but there is also an external program that enforces a schema on the
* registry JSON that should catch all of these problems ahead of time.
*
* @param[in] name - The error name, like xyz.openbmc_project.Error.Foo
- *
+ * - OR
+ * - The reason code, like 0x1001
+ * @param[in] type - LookupType enum value
+ * @param[in] toCache - boolean to cache registry in memory
* @return optional<Entry> A filled in message registry structure if
* found, otherwise an empty optional object.
*/
- std::optional<Entry> lookup(const std::string& name);
+ std::optional<Entry> lookup(const std::string& name, LookupType type,
+ bool toCache = false);
private:
/**
+ * @brief Parse message registry file using nlohmann::json
+ * @param[in] registryFile - The message registry JSON file
+ * @return optional<nlohmann::json> The full message registry object or an
+ * empty optional object upon failure.
+ */
+ std::optional<nlohmann::json>
+ readRegistry(const std::filesystem::path& registryFile);
+
+ /**
* @brief The path to the registry JSON file.
*/
std::filesystem::path _registryFile;
+
+ /**
+ * @brief The full message registry object.
+ */
+ std::optional<nlohmann::json> _registry;
};
namespace helper
diff --git a/extensions/openpower-pels/src.cpp b/extensions/openpower-pels/src.cpp
index b2b86bf..570c6c6 100644
--- a/extensions/openpower-pels/src.cpp
+++ b/extensions/openpower-pels/src.cpp
@@ -15,13 +15,18 @@
*/
#include "src.hpp"
+#include "json_utils.hpp"
+#include "paths.hpp"
+#include "pel_values.hpp"
+
#include <phosphor-logging/log.hpp>
namespace openpower
{
namespace pels
{
-
+namespace pv = openpower::pels::pel_values;
+namespace rg = openpower::pels::message;
using namespace phosphor::logging;
void SRC::unflatten(Stream& stream)
@@ -177,5 +182,256 @@ void SRC::validate()
_valid = failed ? false : true;
}
+std::optional<std::string> SRC::getErrorDetails(message::Registry& registry,
+ DetailLevel type,
+ bool toCache) const
+{
+ const std::string jsonIndent(indentLevel, 0x20);
+ std::string errorOut;
+ uint8_t errorType =
+ strtoul(asciiString().substr(0, 2).c_str(), nullptr, 16);
+ if (errorType == static_cast<uint8_t>(SRCType::bmcError) ||
+ errorType == static_cast<uint8_t>(SRCType::powerError))
+ {
+ auto entry = registry.lookup("0x" + asciiString().substr(4, 4),
+ rg::LookupType::reasonCode, toCache);
+ if (entry)
+ {
+ errorOut.append(jsonIndent + "\"Error Details\": {\n");
+ auto errorMsg = getErrorMessage(*entry);
+ if (errorMsg)
+ {
+ if (type == DetailLevel::message)
+ {
+ return errorMsg.value();
+ }
+ else
+ {
+ jsonInsert(errorOut, "Message", errorMsg.value(), 2);
+ }
+ }
+ if (entry->src.hexwordADFields)
+ {
+ std::map<size_t, std::string> adFields =
+ entry->src.hexwordADFields.value();
+ for (const auto& hexwordMap : adFields)
+ {
+ jsonInsert(errorOut, hexwordMap.second,
+ getNumberString("0x%X",
+ _hexData[getWordIndexFromWordNum(
+ hexwordMap.first)]),
+ 2);
+ }
+ }
+ errorOut.erase(errorOut.size() - 2);
+ errorOut.append("\n");
+ errorOut.append(jsonIndent + "},\n");
+ return errorOut;
+ }
+ }
+ return std::nullopt;
+}
+
+std::optional<std::string>
+ SRC::getErrorMessage(const message::Entry& regEntry) const
+{
+ try
+ {
+ if (regEntry.doc.messageArgSources)
+ {
+ size_t msgLen = regEntry.doc.message.length();
+ char msg[msgLen + 1];
+ strcpy(msg, regEntry.doc.message.c_str());
+ std::vector<uint32_t> argSourceVals;
+ std::string message;
+ const auto& argValues = regEntry.doc.messageArgSources.value();
+ for (size_t i = 0; i < argValues.size(); ++i)
+ {
+ argSourceVals.push_back(_hexData[getWordIndexFromWordNum(
+ argValues[i].back() - '0')]);
+ }
+ const char* msgPointer = msg;
+ while (*msgPointer)
+ {
+ if (*msgPointer == '%')
+ {
+ msgPointer++;
+ size_t wordIndex = *msgPointer - '0';
+ if (isdigit(*msgPointer) && wordIndex >= 1 &&
+ static_cast<uint16_t>(wordIndex) <=
+ argSourceVals.size())
+ {
+ message.append(getNumberString(
+ "0x%X", argSourceVals[wordIndex - 1]));
+ }
+ else
+ {
+ message.append("%" + std::string(1, *msgPointer));
+ }
+ }
+ else
+ {
+ message.push_back(*msgPointer);
+ }
+ msgPointer++;
+ }
+ return message;
+ }
+ else
+ {
+ return regEntry.doc.message;
+ }
+ }
+ catch (const std::exception& e)
+ {
+ log<level::ERR>("Cannot get error message from registry entry",
+ entry("ERROR=%s", e.what()));
+ }
+ return std::nullopt;
+}
+
+std::optional<std::string> SRC::getCallouts() const
+{
+ if (!_callouts)
+ {
+ return std::nullopt;
+ }
+ std::string printOut;
+ const std::string jsonIndent(indentLevel, 0x20);
+ const auto& callout = _callouts->callouts();
+ const auto& compDescrp = pv::failingComponentType;
+ printOut.append(jsonIndent + "\"Callout Section\": {\n");
+ jsonInsert(printOut, "Callout Count", std::to_string(callout.size()), 2);
+ printOut.append(jsonIndent + jsonIndent + "\"Callouts\": [");
+ for (auto& entry : callout)
+ {
+ printOut.append("{\n");
+ if (entry->fruIdentity())
+ {
+ jsonInsert(
+ printOut, "FRU Type",
+ compDescrp.at(entry->fruIdentity()->failingComponentType()), 3);
+ jsonInsert(printOut, "Priority",
+ pv::getValue(entry->priority(),
+ pel_values::calloutPriorityValues),
+ 3);
+ if (!entry->locationCode().empty())
+ {
+ jsonInsert(printOut, "Location Code", entry->locationCode(), 3);
+ }
+ if (entry->fruIdentity()->getPN().has_value())
+ {
+ jsonInsert(printOut, "Part Number",
+ entry->fruIdentity()->getPN().value(), 3);
+ }
+ if (entry->fruIdentity()->getMaintProc().has_value())
+ {
+ jsonInsert(printOut, "Procedure Number",
+ entry->fruIdentity()->getMaintProc().value(), 3);
+ if (pv::procedureDesc.find(
+ entry->fruIdentity()->getMaintProc().value()) !=
+ pv::procedureDesc.end())
+ {
+ jsonInsert(
+ printOut, "Description",
+ pv::procedureDesc.at(
+ entry->fruIdentity()->getMaintProc().value()),
+ 3);
+ }
+ }
+ if (entry->fruIdentity()->getCCIN().has_value())
+ {
+ jsonInsert(printOut, "CCIN",
+ entry->fruIdentity()->getCCIN().value(), 3);
+ }
+ if (entry->fruIdentity()->getSN().has_value())
+ {
+ jsonInsert(printOut, "Serial Number",
+ entry->fruIdentity()->getSN().value(), 3);
+ }
+ }
+ if (entry->pceIdentity())
+ {
+ const auto& pceIdentMtms = entry->pceIdentity()->mtms();
+ if (!pceIdentMtms.machineTypeAndModel().empty())
+ {
+ jsonInsert(printOut, "PCE MTMS",
+ pceIdentMtms.machineTypeAndModel() + "_" +
+ pceIdentMtms.machineSerialNumber(),
+ 3);
+ }
+ if (!entry->pceIdentity()->enclosureName().empty())
+ {
+ jsonInsert(printOut, "PCE Name",
+ entry->pceIdentity()->enclosureName(), 3);
+ }
+ }
+ if (entry->mru())
+ {
+ const auto& mruCallouts = entry->mru()->mrus();
+ std::string mruId;
+ for (auto& element : mruCallouts)
+ {
+ if (!mruId.empty())
+ {
+ mruId.append(", " + getNumberString("%08X", element.id));
+ }
+ else
+ {
+ mruId.append(getNumberString("%08X", element.id));
+ }
+ }
+ jsonInsert(printOut, "MRU Id", mruId, 3);
+ }
+ printOut.erase(printOut.size() - 2);
+ printOut.append("\n" + jsonIndent + jsonIndent + "}, ");
+ };
+ printOut.erase(printOut.size() - 2);
+ printOut.append("]\n" + jsonIndent + "}");
+ return printOut;
+}
+
+std::optional<std::string> SRC::getJSON() const
+{
+ std::string ps;
+ jsonInsert(ps, "Section Version", getNumberString("%d", _header.version),
+ 1);
+ jsonInsert(ps, "Sub-section type", getNumberString("%d", _header.subType),
+ 1);
+ jsonInsert(ps, "Created by", getNumberString("0x%X", _header.componentID),
+ 1);
+ jsonInsert(ps, "SRC Version", getNumberString("0x%02X", _version), 1);
+ jsonInsert(ps, "SRC Format", getNumberString("0x%02X", _hexData[0]), 1);
+ jsonInsert(ps, "Power Control Net Fault",
+ pv::boolString.at(isPowerFaultEvent()), 1);
+ rg::Registry registry(getMessageRegistryPath() / rg::registryFileName);
+ auto errorDetails = getErrorDetails(registry, DetailLevel::json);
+ if (errorDetails)
+ {
+ ps.append(errorDetails.value());
+ }
+ jsonInsert(ps, "Valid Word Count", getNumberString("0x%02X", _wordCount),
+ 1);
+ std::string refcode = asciiString();
+ refcode = refcode.substr(0, refcode.find(0x20));
+ jsonInsert(ps, "Reference Code", refcode, 1);
+ for (size_t i = 2; i <= _wordCount; i++)
+ {
+ jsonInsert(
+ ps, "Hex Word " + std::to_string(i),
+ getNumberString("%08X", _hexData[getWordIndexFromWordNum(i)]), 1);
+ }
+ auto calloutJson = getCallouts();
+ if (calloutJson)
+ {
+ ps.append(calloutJson.value());
+ }
+ else
+ {
+ ps.erase(ps.size() - 2);
+ }
+ return ps;
+}
+
} // namespace pels
} // namespace openpower
diff --git a/extensions/openpower-pels/src.hpp b/extensions/openpower-pels/src.hpp
index dada63a..2296f6f 100644
--- a/extensions/openpower-pels/src.hpp
+++ b/extensions/openpower-pels/src.hpp
@@ -21,6 +21,11 @@ constexpr uint8_t bmcSRCFormat = 0x55;
constexpr uint8_t primaryBMCPosition = 0x10;
constexpr size_t baseSRCSize = 72;
+enum class DetailLevel
+{
+ message = 0x01,
+ json = 0x02
+};
/**
* @class SRC
*
@@ -45,7 +50,10 @@ class SRC : public Section
enum HeaderFlags
{
additionalSections = 0x01,
- powerFaultEvent = 0x02
+ powerFaultEvent = 0x02,
+ hypDumpInit = 0x04,
+ i5OSServiceEventBit = 0x10,
+ virtualProgressSRC = 0x80
};
SRC() = delete;
@@ -212,6 +220,23 @@ class SRC : public Section
return wordNum - 2;
}
+ /**
+ * @brief Get section in JSON.
+ * @return std::optional<std::string> - SRC section's JSON
+ */
+ std::optional<std::string> getJSON() const override;
+
+ /**
+ * @brief Get error details based on refcode and hexwords
+ * @param[in] registry - Registry object
+ * @param[in] type - detail level enum value : single message or full json
+ * @param[in] toCache - boolean to cache registry in memory, default=false
+ * @return std::optional<std::string> - Error details
+ */
+ std::optional<std::string> getErrorDetails(message::Registry& registry,
+ DetailLevel type,
+ bool toCache = false) const;
+
private:
/**
* @brief Fills in the user defined hex words from the
@@ -281,6 +306,20 @@ class SRC : public Section
void validate() override;
/**
+ * @brief Get error description from message registry
+ * @param[in] regEntry - The message registry entry for the error
+ * @return std::optional<std::string> - Error message
+ */
+ std::optional<std::string>
+ getErrorMessage(const message::Entry& regEntry) const;
+
+ /**
+ * @brief Get Callout info in JSON
+ * @return std::optional<std::string> - Callout details
+ */
+ std::optional<std::string> getCallouts() const;
+
+ /**
* @brief The SRC version field
*/
uint8_t _version;
diff --git a/extensions/openpower-pels/tools/peltool.cpp b/extensions/openpower-pels/tools/peltool.cpp
index 5c040ca..f1d5f59 100644
--- a/extensions/openpower-pels/tools/peltool.cpp
+++ b/extensions/openpower-pels/tools/peltool.cpp
@@ -16,6 +16,7 @@
#include "config.h"
#include "../bcd_time.hpp"
+#include "../paths.hpp"
#include "../pel.hpp"
#include "../pel_types.hpp"
#include "../pel_values.hpp"
@@ -197,7 +198,7 @@ std::string trim(std::string s, const char* t = ws)
}
template <typename T>
-std::string genPELJSON(T itr, bool hidden)
+std::string genPELJSON(T itr, bool hidden, message::Registry& registry)
{
std::size_t found;
std::string val;
@@ -224,9 +225,24 @@ std::string genPELJSON(T itr, bool hidden)
val = std::string(tmpValStr);
listStr += "\t\"" + val + "\": {\n";
// ASCII
- val = pel.primarySRC() ? pel.primarySRC().value()->asciiString()
- : "No SRC";
- listStr += "\t\t\"SRC\": \"" + trim(val) + "\",\n";
+ if (pel.primarySRC())
+ {
+ val = pel.primarySRC().value()->asciiString();
+ listStr += "\t\t\"SRC\": \"" +
+ val.substr(0, val.find(0x20)) + "\",\n";
+ // Registry message
+ auto regVal = pel.primarySRC().value()->getErrorDetails(
+ registry, DetailLevel::message, true);
+ if (regVal)
+ {
+ val = regVal.value();
+ listStr += "\t\t\"Message\": \"" + val + "\",\n";
+ }
+ }
+ else
+ {
+ listStr += "\t\t\"SRC\": \"No SRC\",\n";
+ }
// platformid
sprintf(tmpValStr, "0x%X", pel.privateHeader().plid());
val = std::string(tmpValStr);
@@ -308,8 +324,10 @@ void printList(bool order, bool hidden)
fileNameToTimestamp((*it).path().filename()));
}
}
- auto buildJSON = [&listStr, &hidden](const auto& i) {
- listStr += genPELJSON(i, hidden);
+ message::Registry registry(getMessageRegistryPath() /
+ message::registryFileName);
+ auto buildJSON = [&listStr, &hidden, &registry](const auto& i) {
+ listStr += genPELJSON(i, hidden, registry);
};
if (order)
{
diff --git a/test/openpower-pels/Makefile.include b/test/openpower-pels/Makefile.include
index 6905dc3..2bed701 100644
--- a/test/openpower-pels/Makefile.include
+++ b/test/openpower-pels/Makefile.include
@@ -289,9 +289,13 @@ src_test_LDADD = \
$(top_builddir)/extensions/openpower-pels/callout.o \
$(top_builddir)/extensions/openpower-pels/callouts.o \
$(top_builddir)/extensions/openpower-pels/fru_identity.o \
+ $(top_builddir)/extensions/openpower-pels/json_utils.o \
+ $(top_builddir)/extensions/openpower-pels/paths.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/pel_values.o \
+ $(top_builddir)/extensions/openpower-pels/registry.o \
$(top_builddir)/extensions/openpower-pels/src.o
src_test_LDFLAGS = $(test_ldflags)
@@ -309,9 +313,13 @@ extended_user_header_test_LDADD = \
$(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/json_utils.o \
$(top_builddir)/extensions/openpower-pels/mru.o \
$(top_builddir)/extensions/openpower-pels/mtms.o \
+ $(top_builddir)/extensions/openpower-pels/paths.o \
+ $(top_builddir)/extensions/openpower-pels/pel_values.o \
$(top_builddir)/extensions/openpower-pels/pce_identity.o \
+ $(top_builddir)/extensions/openpower-pels/registry.o \
$(top_builddir)/extensions/openpower-pels/src.o
extended_user_header_test_LDFLAGS = $(test_ldflags)
diff --git a/test/openpower-pels/pel_manager_test.cpp b/test/openpower-pels/pel_manager_test.cpp
index 8f3df22..08124ff 100644
--- a/test/openpower-pels/pel_manager_test.cpp
+++ b/test/openpower-pels/pel_manager_test.cpp
@@ -122,6 +122,11 @@ TEST_F(ManagerTest, TestCreateWithMessageRegistry)
"SRC":
{
"ReasonCode": "0x2030"
+ },
+ "Documentation":
+ {
+ "Description": "A PGOOD Fault",
+ "Message": "PS had a PGOOD Fault"
}
}
]
diff --git a/test/openpower-pels/registry_test.cpp b/test/openpower-pels/registry_test.cpp
index c193c3f..2944ce0 100644
--- a/test/openpower-pels/registry_test.cpp
+++ b/test/openpower-pels/registry_test.cpp
@@ -35,6 +35,12 @@ const auto registryData = R"(
"SRC":
{
"ReasonCode": "0x2030"
+ },
+
+ "Documentation":
+ {
+ "Description": "A PGOOD Fault",
+ "Message": "PS had a PGOOD Fault"
}
},
@@ -66,6 +72,20 @@ const auto registryData = R"(
"AdditionalDataPropSource": "VOLTAGE"
}
}
+ },
+
+ "Documentation":
+ {
+ "Description": "A PGOOD Fault",
+ "Message": "PS %1 had a PGOOD Fault",
+ "MessageArgSources":
+ [
+ "SRCWord6"
+ ],
+ "Notes": [
+ "In the UserData section there is a JSON",
+ "dump that provides debug information."
+ ]
}
}
]
@@ -104,7 +124,7 @@ TEST_F(RegistryTest, TestNoEntry)
auto path = RegistryTest::writeData(registryData);
Registry registry{path};
- auto entry = registry.lookup("foo");
+ auto entry = registry.lookup("foo", LookupType::name);
EXPECT_FALSE(entry);
}
@@ -113,7 +133,8 @@ TEST_F(RegistryTest, TestFindEntry)
auto path = RegistryTest::writeData(registryData);
Registry registry{path};
- auto entry = registry.lookup("xyz.openbmc_project.Power.OverVoltage");
+ auto entry = registry.lookup("xyz.openbmc_project.Power.OverVoltage",
+ LookupType::name);
ASSERT_TRUE(entry);
EXPECT_EQ(entry->name, "xyz.openbmc_project.Power.OverVoltage");
EXPECT_EQ(entry->subsystem, 0x62);
@@ -147,6 +168,17 @@ TEST_F(RegistryTest, TestFindEntry)
EXPECT_NE(std::find((*sid).begin(), (*sid).end(), 5), (*sid).end());
EXPECT_NE(std::find((*sid).begin(), (*sid).end(), 6), (*sid).end());
EXPECT_NE(std::find((*sid).begin(), (*sid).end(), 7), (*sid).end());
+
+ EXPECT_EQ(entry->doc.description, "A PGOOD Fault");
+ EXPECT_EQ(entry->doc.message, "PS %1 had a PGOOD Fault");
+ auto& hexwordSource = entry->doc.messageArgSources;
+ EXPECT_TRUE(hexwordSource);
+ EXPECT_EQ((*hexwordSource).size(), 1);
+ EXPECT_EQ((*hexwordSource).front(), "SRCWord6");
+
+ entry = registry.lookup("0x2333", LookupType::reasonCode);
+ ASSERT_TRUE(entry);
+ EXPECT_EQ(entry->name, "xyz.openbmc_project.Power.OverVoltage");
}
// Check the entry that mostly uses defaults
@@ -155,7 +187,8 @@ TEST_F(RegistryTest, TestFindEntryMinimal)
auto path = RegistryTest::writeData(registryData);
Registry registry{path};
- auto entry = registry.lookup("xyz.openbmc_project.Power.Fault");
+ auto entry =
+ registry.lookup("xyz.openbmc_project.Power.Fault", LookupType::name);
ASSERT_TRUE(entry);
EXPECT_EQ(entry->name, "xyz.openbmc_project.Power.Fault");
EXPECT_EQ(entry->subsystem, 0x61);
@@ -180,7 +213,7 @@ TEST_F(RegistryTest, TestBadJSON)
Registry registry{path};
- EXPECT_FALSE(registry.lookup("foo"));
+ EXPECT_FALSE(registry.lookup("foo", LookupType::name));
}
// Test the helper functions the use the pel_values data.
OpenPOWER on IntegriCloud