diff options
author | Matt Spinler <spinler@us.ibm.com> | 2019-09-20 11:16:15 -0500 |
---|---|---|
committer | Matt Spinler <spinler@us.ibm.com> | 2019-10-09 13:11:51 +0000 |
commit | 93e2932f0667d54926e933eedab7ee2adc8b2731 (patch) | |
tree | 975c52234d2a15e0872fefe177ac0cd8ae0e78e2 | |
parent | 367144cfa0eb857f4cb9ba786f43933469e19e34 (diff) | |
download | phosphor-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.hpp | 6 | ||||
-rw-r--r-- | extensions/openpower-pels/registry.cpp | 176 | ||||
-rw-r--r-- | extensions/openpower-pels/registry.hpp | 125 | ||||
-rw-r--r-- | test/openpower-pels/registry_test.cpp | 125 |
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); +} |