/** * 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 "registry.hpp" #include "pel_types.hpp" #include "pel_values.hpp" #include #include namespace openpower { namespace pels { namespace message { namespace pv = pel_values; namespace fs = std::filesystem; using namespace phosphor::logging; constexpr auto debugFilePath = "/etc/phosphor-logging/"; namespace helper { uint8_t getSubsystem(const std::string& subsystemName) { // Get the actual value to use in the PEL for the string name auto ss = pv::findByName(subsystemName, pv::subsystemValues); if (ss == pv::subsystemValues.end()) { // Schema validation should be catching this. log("Invalid subsystem name used in message registry", entry("SUBSYSTEM=%s", subsystemName.c_str())); throw std::runtime_error("Invalid subsystem used in message registry"); } return std::get(*ss); } uint8_t getSeverity(const std::string& severityName) { auto s = pv::findByName(severityName, pv::severityValues); if (s == pv::severityValues.end()) { // Schema validation should be catching this. log("Invalid severity name used in message registry", entry("SEVERITY=%s", severityName.c_str())); throw std::runtime_error("Invalid severity used in message registry"); } return std::get(*s); } uint16_t getActionFlags(const std::vector& flags) { uint16_t actionFlags = 0; // Make the bitmask based on the array of flag names for (const auto& flag : flags) { auto s = pv::findByName(flag, pv::actionFlagsValues); if (s == pv::actionFlagsValues.end()) { // Schema validation should be catching this. log("Invalid action flag name used in message registry", entry("FLAG=%s", flag.c_str())); throw std::runtime_error( "Invalid action flag used in message registry"); } actionFlags |= std::get(*s); } return actionFlags; } uint8_t getEventType(const std::string& eventTypeName) { auto t = pv::findByName(eventTypeName, pv::eventTypeValues); if (t == pv::eventTypeValues.end()) { log("Invalid event type used in message registry", entry("EVENT_TYPE=%s", eventTypeName.c_str())); throw std::runtime_error("Invalid event type used in message registry"); } return std::get(*t); } uint8_t getEventScope(const std::string& eventScopeName) { auto s = pv::findByName(eventScopeName, pv::eventScopeValues); if (s == pv::eventScopeValues.end()) { log("Invalid event scope used in registry", entry("EVENT_SCOPE=%s", eventScopeName.c_str())); throw std::runtime_error( "Invalid event scope used in message registry"); } return std::get(*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( "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( "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> getSRCHexwordFields(const nlohmann::json& src, const std::string& name) { std::map 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( "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> getSRCSymptomIDFields(const nlohmann::json& src, const std::string& name) { std::vector 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( "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(SRCType::bmcError)) { id = reasonCode & 0xFF00; } else { log("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 Registry::lookup(const std::string& name, LookupType type, bool toCache) { std::optional registryTmp; auto& registryOpt = (_registry) ? _registry : registryTmp; if (!registryOpt) { 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, &type](const auto& j) { return ((name == j["Name"] && type == LookupType::name) || (name == j["SRC"]["ReasonCode"] && type == LookupType::reasonCode)); }); if (e != registry["PELs"].end()) { // Fill in the Entry structure from the JSON. Most, but not all, fields // are optional. try { Entry entry; entry.name = (*e)["Name"]; entry.subsystem = helper::getSubsystem((*e)["Subsystem"]); if (e->find("ActionFlags") != e->end()) { entry.actionFlags = helper::getActionFlags((*e)["ActionFlags"]); } if (e->find("MfgActionFlags") != e->end()) { entry.mfgActionFlags = helper::getActionFlags((*e)["MfgActionFlags"]); } if (e->find("Severity") != e->end()) { entry.severity = helper::getSeverity((*e)["Severity"]); } if (e->find("MfgSeverity") != e->end()) { entry.mfgSeverity = helper::getSeverity((*e)["MfgSeverity"]); } if (e->find("EventType") != e->end()) { entry.eventType = helper::getEventType((*e)["EventType"]); } if (e->find("EventScope") != e->end()) { entry.eventScope = helper::getEventScope((*e)["EventScope"]); } 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(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"]; } 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) { log("Found invalid message registry field", entry("ERROR=%s", e.what())); } } return std::nullopt; } std::optional 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("Using debug PEL message registry"); file.open(debugFile); } else { file.open(registryFile); } try { registry = nlohmann::json::parse(file); } catch (std::exception& e) { log("Error parsing message registry JSON", entry("JSON_ERROR=%s", e.what())); return std::nullopt; } return registry; } } // namespace message } // namespace pels } // namespace openpower