summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatt Spinler <spinler@us.ibm.com>2019-09-20 11:16:15 -0500
committerMatt Spinler <spinler@us.ibm.com>2019-10-09 13:11:51 +0000
commit93e2932f0667d54926e933eedab7ee2adc8b2731 (patch)
tree975c52234d2a15e0872fefe177ac0cd8ae0e78e2
parent367144cfa0eb857f4cb9ba786f43933469e19e34 (diff)
downloadphosphor-logging-93e2932f0667d54926e933eedab7ee2adc8b2731.tar.gz
phosphor-logging-93e2932f0667d54926e933eedab7ee2adc8b2731.zip
PEL: Read SRC fields out of the registry
The SRC (System Reference code) is a section in the PEL and several of its fields are sourced from the message registry. Signed-off-by: Matt Spinler <spinler@us.ibm.com> Change-Id: I4ca7d18a8225f2667b762015c6fd74bfb59d70ff
-rw-r--r--extensions/openpower-pels/pel_types.hpp6
-rw-r--r--extensions/openpower-pels/registry.cpp176
-rw-r--r--extensions/openpower-pels/registry.hpp125
-rw-r--r--test/openpower-pels/registry_test.cpp125
4 files changed, 429 insertions, 3 deletions
diff --git a/extensions/openpower-pels/pel_types.hpp b/extensions/openpower-pels/pel_types.hpp
index caa75c2..b836942 100644
--- a/extensions/openpower-pels/pel_types.hpp
+++ b/extensions/openpower-pels/pel_types.hpp
@@ -35,5 +35,11 @@ enum class SectionID
extUserData = 0x4544 // 'ED'
};
+enum class SRCType
+{
+ bmcError = 0xBD,
+ powerError = 0x11
+};
+
} // namespace pels
} // namespace openpower
diff --git a/extensions/openpower-pels/registry.cpp b/extensions/openpower-pels/registry.cpp
index 42ced0c..f0efb3e 100644
--- a/extensions/openpower-pels/registry.cpp
+++ b/extensions/openpower-pels/registry.cpp
@@ -104,6 +104,149 @@ uint8_t getEventScope(const std::string& eventScopeName)
return std::get<pv::fieldValuePos>(*s);
}
+uint16_t getSRCReasonCode(const nlohmann::json& src, const std::string& name)
+{
+ std::string rc = src["ReasonCode"];
+ uint16_t reasonCode = strtoul(rc.c_str(), nullptr, 16);
+ if (reasonCode == 0)
+ {
+ log<phosphor::logging::level::ERR>(
+ "Invalid reason code in message registry",
+ entry("ERROR_NAME=%s", name.c_str()),
+ entry("REASON_CODE=%s", rc.c_str()));
+
+ throw std::runtime_error("Invalid reason code in message registry");
+ }
+ return reasonCode;
+}
+
+uint8_t getSRCType(const nlohmann::json& src, const std::string& name)
+{
+ // Looks like: "22"
+ std::string srcType = src["Type"];
+ size_t type = strtoul(srcType.c_str(), nullptr, 16);
+ if ((type == 0) || (srcType.size() != 2)) // 1 hex byte
+ {
+ log<phosphor::logging::level::ERR>(
+ "Invalid SRC Type in message registry",
+ entry("ERROR_NAME=%s", name.c_str()),
+ entry("SRC_TYPE=%s", srcType.c_str()));
+
+ throw std::runtime_error("Invalid SRC Type in message registry");
+ }
+
+ return type;
+}
+
+std::optional<std::map<SRC::WordNum, SRC::AdditionalDataField>>
+ getSRCHexwordFields(const nlohmann::json& src, const std::string& name)
+{
+ std::map<SRC::WordNum, SRC::AdditionalDataField> hexwordFields;
+
+ // Build the map of which AdditionalData fields to use for which SRC words
+
+ // Like:
+ // {
+ // "8":
+ // {
+ // "AdditionalDataPropSource": "TEST"
+ // }
+ //
+ // }
+
+ for (const auto& word : src["Words6To9"].items())
+ {
+ std::string num = word.key();
+ size_t wordNum = std::strtoul(num.c_str(), nullptr, 10);
+
+ if (wordNum == 0)
+ {
+ log<phosphor::logging::level::ERR>(
+ "Invalid SRC word number in message registry",
+ entry("ERROR_NAME=%s", name.c_str()),
+ entry("SRC_WORD_NUM=%s", num.c_str()));
+
+ throw std::runtime_error("Invalid SRC word in message registry");
+ }
+
+ auto attributes = word.value();
+ std::string adPropName = attributes["AdditionalDataPropSource"];
+ hexwordFields[wordNum] = std::move(adPropName);
+ }
+
+ if (!hexwordFields.empty())
+ {
+ return hexwordFields;
+ }
+
+ return std::nullopt;
+}
+std::optional<std::vector<SRC::WordNum>>
+ getSRCSymptomIDFields(const nlohmann::json& src, const std::string& name)
+{
+ std::vector<SRC::WordNum> symptomIDFields;
+
+ // Looks like:
+ // "SymptomIDFields": ["SRCWord3", "SRCWord6"],
+
+ for (const std::string& field : src["SymptomIDFields"])
+ {
+ // Just need the last digit off the end, e.g. SRCWord6.
+ // The schema enforces the format of these.
+ auto srcWordNum = field.substr(field.size() - 1);
+ size_t num = std::strtoul(srcWordNum.c_str(), nullptr, 10);
+ if (num == 0)
+ {
+ log<phosphor::logging::level::ERR>(
+ "Invalid symptom ID field in message registry",
+ entry("ERROR_NAME=%s", name.c_str()),
+ entry("FIELD_NAME=%s", srcWordNum.c_str()));
+
+ throw std::runtime_error("Invalid symptom ID in message registry");
+ }
+ symptomIDFields.push_back(num);
+ }
+ if (!symptomIDFields.empty())
+ {
+ return symptomIDFields;
+ }
+
+ return std::nullopt;
+}
+
+uint16_t getComponentID(uint8_t srcType, uint16_t reasonCode,
+ const nlohmann::json& pelEntry, const std::string& name)
+{
+ uint16_t id = 0;
+
+ // If the ComponentID field is there, use that. Otherwise, if it's a
+ // 0xBD BMC error SRC, use the reasoncode.
+ if (pelEntry.find("ComponentID") != pelEntry.end())
+ {
+ std::string componentID = pelEntry["ComponentID"];
+ id = strtoul(componentID.c_str(), nullptr, 16);
+ }
+ else
+ {
+ // On BMC error SRCs (BD), can just get the component ID from
+ // the first byte of the reason code.
+ if (srcType == static_cast<uint8_t>(SRCType::bmcError))
+ {
+ id = reasonCode & 0xFF00;
+ }
+ else
+ {
+ log<level::ERR>("Missing component ID field in message registry",
+ entry("ERROR_NAME=%s", name.c_str()));
+
+ throw std::runtime_error(
+ "Missing component ID field in message registry");
+ }
+ }
+
+ return id;
+}
+
} // namespace helper
std::optional<Entry> Registry::lookup(const std::string& name)
@@ -176,7 +319,38 @@ std::optional<Entry> Registry::lookup(const std::string& name)
entry.eventScope = helper::getEventScope((*e)["EventScope"]);
}
- // TODO: SRC fields
+ auto& src = (*e)["SRC"];
+ entry.src.reasonCode = helper::getSRCReasonCode(src, name);
+
+ if (src.find("Type") != src.end())
+ {
+ entry.src.type = helper::getSRCType(src, name);
+ }
+ else
+ {
+ entry.src.type = static_cast<uint8_t>(SRCType::bmcError);
+ }
+
+ // Now that we know the SRC type and reason code,
+ // we can get the component ID.
+ entry.componentID = helper::getComponentID(
+ entry.src.type, entry.src.reasonCode, *e, name);
+
+ if (src.find("Words6To9") != src.end())
+ {
+ entry.src.hexwordADFields =
+ helper::getSRCHexwordFields(src, name);
+ }
+
+ if (src.find("SymptomIDFields") != src.end())
+ {
+ entry.src.symptomID = helper::getSRCSymptomIDFields(src, name);
+ }
+
+ if (src.find("PowerFault") != src.end())
+ {
+ entry.src.powerFault = src["PowerFault"];
+ }
return entry;
}
diff --git a/extensions/openpower-pels/registry.hpp b/extensions/openpower-pels/registry.hpp
index e56bd42..c55c624 100644
--- a/extensions/openpower-pels/registry.hpp
+++ b/extensions/openpower-pels/registry.hpp
@@ -15,6 +15,55 @@ namespace message
constexpr auto registryFileName = "message_registry.json";
/**
+ * @brief Represents the SRC 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 SRC
+{
+ /**
+ * @brief SRC type - The first byte of the ASCII string
+ */
+ uint8_t type;
+
+ /**
+ * @brief The SRC reason code (2nd half of 4B 'ASCII string' word)
+ */
+ uint16_t reasonCode;
+
+ /**
+ * @brief Specifies if the SRC represents a power fault. Optional.
+ */
+ std::optional<bool> powerFault;
+
+ /**
+ * @brief An optional vector of SRC hexword numbers that should be used
+ * along with the SRC ASCII string to build the Symptom ID, which
+ * is a field in the Extended Header section.
+ */
+ using WordNum = size_t;
+ std::optional<std::vector<WordNum>> symptomID;
+
+ /**
+ * @brief Which AdditionalData fields to use to fill in the user defined
+ * SRC hexwords.
+ *
+ * For example, if the AdditionalData event log property contained
+ * "CHIPNUM=42" and this map contained {6, CHIPNUM}, then the code
+ * would put 42 into SRC hexword 6.
+ */
+ using AdditionalDataField = std::string;
+ std::optional<std::map<WordNum, AdditionalDataField>> hexwordADFields;
+
+ SRC() : type(0), reasonCode(0)
+ {
+ }
+};
+
+/**
* @brief Represents a message registry entry, which is used for creating a
* PEL from an OpenBMC event log.
*/
@@ -26,6 +75,11 @@ struct Entry
std::string name;
/**
+ * @brief The component ID of the PEL creator.
+ */
+ uint16_t componentID;
+
+ /**
* @brief The PEL subsystem field.
*/
uint8_t subsystem;
@@ -65,7 +119,10 @@ struct Entry
*/
std::optional<uint8_t> eventScope;
- // TODO: SRC related fields
+ /**
+ * The SRC related fields.
+ */
+ SRC src;
};
/**
@@ -171,6 +228,72 @@ uint8_t getEventType(const std::string& eventTypeName);
*/
uint8_t getEventScope(const std::string& eventScopeName);
+/**
+ * @brief Reads the "ReasonCode" field out of JSON and converts the string value
+ * such as "0x5555" to a uint16 like 0x5555.
+ *
+ * @param[in] src - The message registry SRC dictionary to read from
+ * @param[in] name - The error name, to use in a trace if things go awry.
+ *
+ * @return uint16_t - The reason code
+ */
+uint16_t getSRCReasonCode(const nlohmann::json& src, const std::string& name);
+
+/**
+ * @brief Reads the "Type" field out of JSON and converts it to the SRC::Type
+ * value.
+ *
+ * @param[in] src - The message registry SRC dictionary to read from
+ * @param[in] name - The error name, to use in a trace if things go awry.
+ *
+ * @return uint8_t - The SRC type value, like 0x11
+ */
+uint8_t getSRCType(const nlohmann::json& src, const std::string& name);
+
+/**
+ * @brief Reads the "Words6To9" field out of JSON and converts it to a map
+ * of the SRC word number to the AdditionalData property field used
+ * to fill it in with.
+ *
+ * @param[in] src - The message registry SRC dictionary to read from
+ * @param[in] name - The error name, to use in a trace if things go awry.
+ *
+ * @return std::optional<std::map<SRC::WordNum, SRC::AdditionalDataField>>
+ */
+std::optional<std::map<SRC::WordNum, SRC::AdditionalDataField>>
+ getSRCHexwordFields(const nlohmann::json& src, const std::string& name);
+
+/**
+ * @brief Reads the "SymptomIDFields" field out of JSON and converts it to
+ * a vector of SRC word numbers.
+ *
+ * @param[in] src - The message registry SRC dictionary to read from
+ * @param[in] name - The error name, to use in a trace if things go awry.
+ *
+ * @return std::optional<std::vector<SRC::WordNum>>
+ */
+std::optional<std::vector<SRC::WordNum>>
+ getSRCSymptomIDFields(const nlohmann::json& src, const std::string& name);
+
+/**
+ * @brief Reads the "ComponentID" field out of JSON and converts it to a
+ * uint16_t like 0xFF00.
+ *
+ * The ComponentID JSON field is only required if the SRC type isn't a BD
+ * BMC SRC, because for those SRCs it can be inferred from the upper byte
+ * of the SRC reasoncode.
+ *
+ * @param[in] srcType - The SRC type
+ * @param[in] reasonCode - The SRC reason code
+ * @param[in] pelEntry - The PEL entry JSON
+ * @param[in] name - The error name, to use in a trace if things go awry.
+ *
+ * @return uin16_t - The component ID, like 0xFF00
+ */
+uint16_t getComponentID(uint8_t srcType, uint16_t reasonCode,
+ const nlohmann::json& pelEntry,
+ const std::string& name);
+
} // namespace helper
} // namespace message
diff --git a/test/openpower-pels/registry_test.cpp b/test/openpower-pels/registry_test.cpp
index 2107725..a16370f 100644
--- a/test/openpower-pels/registry_test.cpp
+++ b/test/openpower-pels/registry_test.cpp
@@ -107,10 +107,32 @@ TEST_F(RegistryTest, TestFindEntry)
EXPECT_EQ(*(entry->mfgSeverity), 0x00);
EXPECT_EQ(entry->actionFlags, 0xA800);
EXPECT_EQ(*(entry->mfgActionFlags), 0x4000);
+ EXPECT_EQ(entry->componentID, 0x2300);
EXPECT_FALSE(entry->eventType);
EXPECT_FALSE(entry->eventScope);
- // TODO: compare SRC fields
+ EXPECT_EQ(entry->src.type, 0xBD);
+ EXPECT_EQ(entry->src.reasonCode, 0x2333);
+ EXPECT_EQ(*(entry->src.powerFault), true);
+
+ auto& hexwords = entry->src.hexwordADFields;
+ EXPECT_TRUE(hexwords);
+ EXPECT_EQ((*hexwords).size(), 2);
+
+ auto word = (*hexwords).find(6);
+ EXPECT_NE(word, (*hexwords).end());
+ EXPECT_EQ(word->second, "PS_NUM");
+
+ word = (*hexwords).find(7);
+ EXPECT_NE(word, (*hexwords).end());
+ EXPECT_EQ(word->second, "VOLTAGE");
+
+ auto& sid = entry->src.symptomID;
+ EXPECT_TRUE(sid);
+ EXPECT_EQ((*sid).size(), 3);
+ 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());
}
// Check the entry that mostly uses defaults
@@ -127,8 +149,15 @@ TEST_F(RegistryTest, TestFindEntryMinimal)
EXPECT_FALSE(entry->mfgSeverity);
EXPECT_FALSE(entry->mfgActionFlags);
EXPECT_EQ(entry->actionFlags, 0xA000);
+ EXPECT_EQ(entry->componentID, 0x2000);
EXPECT_FALSE(entry->eventType);
EXPECT_FALSE(entry->eventScope);
+
+ EXPECT_EQ(entry->src.reasonCode, 0x2030);
+ EXPECT_EQ(entry->src.type, 0xBD);
+ EXPECT_FALSE(entry->src.powerFault);
+ EXPECT_FALSE(entry->src.hexwordADFields);
+ EXPECT_FALSE(entry->src.symptomID);
}
TEST_F(RegistryTest, TestBadJSON)
@@ -164,3 +193,97 @@ TEST_F(RegistryTest, TestHelperFunctions)
flags.push_back("foo");
EXPECT_THROW(getActionFlags(flags), std::runtime_error);
}
+
+TEST_F(RegistryTest, TestGetSRCReasonCode)
+{
+ using namespace openpower::pels::message::helper;
+ EXPECT_EQ(getSRCReasonCode(R"({"ReasonCode": "0x5555"})"_json, "foo"),
+ 0x5555);
+
+ EXPECT_THROW(getSRCReasonCode(R"({"ReasonCode": "ZZZZ"})"_json, "foo"),
+ std::runtime_error);
+}
+
+TEST_F(RegistryTest, TestGetSRCType)
+{
+ using namespace openpower::pels::message::helper;
+ EXPECT_EQ(getSRCType(R"({"Type": "11"})"_json, "foo"), 0x11);
+ EXPECT_EQ(getSRCType(R"({"Type": "BF"})"_json, "foo"), 0xBF);
+
+ EXPECT_THROW(getSRCType(R"({"Type": "1"})"_json, "foo"),
+ std::runtime_error);
+
+ EXPECT_THROW(getSRCType(R"({"Type": "111"})"_json, "foo"),
+ std::runtime_error);
+}
+
+TEST_F(RegistryTest, TestGetSRCHexwordFields)
+{
+ using namespace openpower::pels::message::helper;
+ const auto hexwords = R"(
+ {"Words6To9":
+ {
+ "8":
+ {
+ "AdditionalDataPropSource": "TEST"
+ }
+ }
+ })"_json;
+
+ auto fields = getSRCHexwordFields(hexwords, "foo");
+ EXPECT_TRUE(fields);
+ auto word = fields->find(8);
+ EXPECT_NE(word, fields->end());
+
+ const auto theInvalidRWord = R"(
+ {"Words6To9":
+ {
+ "R":
+ {
+ "AdditionalDataPropSource": "TEST"
+ }
+ }
+ })"_json;
+
+ EXPECT_THROW(getSRCHexwordFields(theInvalidRWord, "foo"),
+ std::runtime_error);
+}
+
+TEST_F(RegistryTest, TestGetSRCSymptomIDFields)
+{
+ using namespace openpower::pels::message::helper;
+ const auto sID = R"(
+ {
+ "SymptomIDFields": ["SRCWord3", "SRCWord4", "SRCWord5"]
+ })"_json;
+
+ auto fields = getSRCSymptomIDFields(sID, "foo");
+ EXPECT_NE(std::find(fields->begin(), fields->end(), 3), fields->end());
+ EXPECT_NE(std::find(fields->begin(), fields->end(), 4), fields->end());
+ EXPECT_NE(std::find(fields->begin(), fields->end(), 5), fields->end());
+
+ const auto badField = R"(
+ {
+ "SymptomIDFields": ["SRCWord3", "SRCWord4", "SRCWord"]
+ })"_json;
+
+ EXPECT_THROW(getSRCSymptomIDFields(badField, "foo"), std::runtime_error);
+}
+
+TEST_F(RegistryTest, TestGetComponentID)
+{
+ using namespace openpower::pels::message::helper;
+
+ // Get it from the JSON
+ auto id =
+ getComponentID(0xBD, 0x4200, R"({"ComponentID":"0x4200"})"_json, "foo");
+ EXPECT_EQ(id, 0x4200);
+
+ // Get it from the reason code on a 0xBD SRC
+ id = getComponentID(0xBD, 0x6700, R"({})"_json, "foo");
+ EXPECT_EQ(id, 0x6700);
+
+ // Not present on a 0x11 SRC
+ EXPECT_THROW(getComponentID(0x11, 0x8800, R"({})"_json, "foo"),
+ std::runtime_error);
+}
OpenPOWER on IntegriCloud