1367144cfSMatt Spinler #include "registry.hpp" 2367144cfSMatt Spinler 3367144cfSMatt Spinler #include "pel_types.hpp" 4367144cfSMatt Spinler #include "pel_values.hpp" 5367144cfSMatt Spinler 6367144cfSMatt Spinler #include <fstream> 7367144cfSMatt Spinler #include <phosphor-logging/log.hpp> 8367144cfSMatt Spinler 9367144cfSMatt Spinler namespace openpower 10367144cfSMatt Spinler { 11367144cfSMatt Spinler namespace pels 12367144cfSMatt Spinler { 13367144cfSMatt Spinler namespace message 14367144cfSMatt Spinler { 15367144cfSMatt Spinler 16367144cfSMatt Spinler namespace pv = pel_values; 17367144cfSMatt Spinler namespace fs = std::filesystem; 18367144cfSMatt Spinler using namespace phosphor::logging; 19367144cfSMatt Spinler 20367144cfSMatt Spinler constexpr auto debugFilePath = "/etc/phosphor-logging/"; 21367144cfSMatt Spinler 22367144cfSMatt Spinler namespace helper 23367144cfSMatt Spinler { 24367144cfSMatt Spinler 25367144cfSMatt Spinler uint8_t getSubsystem(const std::string& subsystemName) 26367144cfSMatt Spinler { 27367144cfSMatt Spinler // Get the actual value to use in the PEL for the string name 28367144cfSMatt Spinler auto ss = pv::findByName(subsystemName, pv::subsystemValues); 29367144cfSMatt Spinler if (ss == pv::subsystemValues.end()) 30367144cfSMatt Spinler { 31367144cfSMatt Spinler // Schema validation should be catching this. 32367144cfSMatt Spinler log<level::ERR>("Invalid subsystem name used in message registry", 33367144cfSMatt Spinler entry("SUBSYSTEM=%s", subsystemName.c_str())); 34367144cfSMatt Spinler 35367144cfSMatt Spinler throw std::runtime_error("Invalid subsystem used in message registry"); 36367144cfSMatt Spinler } 37367144cfSMatt Spinler 38367144cfSMatt Spinler return std::get<pv::fieldValuePos>(*ss); 39367144cfSMatt Spinler } 40367144cfSMatt Spinler 41367144cfSMatt Spinler uint8_t getSeverity(const std::string& severityName) 42367144cfSMatt Spinler { 43367144cfSMatt Spinler auto s = pv::findByName(severityName, pv::severityValues); 44367144cfSMatt Spinler if (s == pv::severityValues.end()) 45367144cfSMatt Spinler { 46367144cfSMatt Spinler // Schema validation should be catching this. 47367144cfSMatt Spinler log<level::ERR>("Invalid severity name used in message registry", 48367144cfSMatt Spinler entry("SEVERITY=%s", severityName.c_str())); 49367144cfSMatt Spinler 50367144cfSMatt Spinler throw std::runtime_error("Invalid severity used in message registry"); 51367144cfSMatt Spinler } 52367144cfSMatt Spinler 53367144cfSMatt Spinler return std::get<pv::fieldValuePos>(*s); 54367144cfSMatt Spinler } 55367144cfSMatt Spinler 56367144cfSMatt Spinler uint16_t getActionFlags(const std::vector<std::string>& flags) 57367144cfSMatt Spinler { 58367144cfSMatt Spinler uint16_t actionFlags = 0; 59367144cfSMatt Spinler 60367144cfSMatt Spinler // Make the bitmask based on the array of flag names 61367144cfSMatt Spinler for (const auto& flag : flags) 62367144cfSMatt Spinler { 63367144cfSMatt Spinler auto s = pv::findByName(flag, pv::actionFlagsValues); 64367144cfSMatt Spinler if (s == pv::actionFlagsValues.end()) 65367144cfSMatt Spinler { 66367144cfSMatt Spinler // Schema validation should be catching this. 67367144cfSMatt Spinler log<level::ERR>("Invalid action flag name used in message registry", 68367144cfSMatt Spinler entry("FLAG=%s", flag.c_str())); 69367144cfSMatt Spinler 70367144cfSMatt Spinler throw std::runtime_error( 71367144cfSMatt Spinler "Invalid action flag used in message registry"); 72367144cfSMatt Spinler } 73367144cfSMatt Spinler 74367144cfSMatt Spinler actionFlags |= std::get<pv::fieldValuePos>(*s); 75367144cfSMatt Spinler } 76367144cfSMatt Spinler 77367144cfSMatt Spinler return actionFlags; 78367144cfSMatt Spinler } 79367144cfSMatt Spinler 80367144cfSMatt Spinler uint8_t getEventType(const std::string& eventTypeName) 81367144cfSMatt Spinler { 82367144cfSMatt Spinler auto t = pv::findByName(eventTypeName, pv::eventTypeValues); 83367144cfSMatt Spinler if (t == pv::eventTypeValues.end()) 84367144cfSMatt Spinler { 85367144cfSMatt Spinler log<level::ERR>("Invalid event type used in message registry", 86367144cfSMatt Spinler entry("EVENT_TYPE=%s", eventTypeName.c_str())); 87367144cfSMatt Spinler 88367144cfSMatt Spinler throw std::runtime_error("Invalid event type used in message registry"); 89367144cfSMatt Spinler } 90367144cfSMatt Spinler return std::get<pv::fieldValuePos>(*t); 91367144cfSMatt Spinler } 92367144cfSMatt Spinler 93367144cfSMatt Spinler uint8_t getEventScope(const std::string& eventScopeName) 94367144cfSMatt Spinler { 95367144cfSMatt Spinler auto s = pv::findByName(eventScopeName, pv::eventScopeValues); 96367144cfSMatt Spinler if (s == pv::eventScopeValues.end()) 97367144cfSMatt Spinler { 98367144cfSMatt Spinler log<level::ERR>("Invalid event scope used in registry", 99367144cfSMatt Spinler entry("EVENT_SCOPE=%s", eventScopeName.c_str())); 100367144cfSMatt Spinler 101367144cfSMatt Spinler throw std::runtime_error( 102367144cfSMatt Spinler "Invalid event scope used in message registry"); 103367144cfSMatt Spinler } 104367144cfSMatt Spinler return std::get<pv::fieldValuePos>(*s); 105367144cfSMatt Spinler } 106367144cfSMatt Spinler 107*93e2932fSMatt Spinler uint16_t getSRCReasonCode(const nlohmann::json& src, const std::string& name) 108*93e2932fSMatt Spinler { 109*93e2932fSMatt Spinler std::string rc = src["ReasonCode"]; 110*93e2932fSMatt Spinler uint16_t reasonCode = strtoul(rc.c_str(), nullptr, 16); 111*93e2932fSMatt Spinler if (reasonCode == 0) 112*93e2932fSMatt Spinler { 113*93e2932fSMatt Spinler log<phosphor::logging::level::ERR>( 114*93e2932fSMatt Spinler "Invalid reason code in message registry", 115*93e2932fSMatt Spinler entry("ERROR_NAME=%s", name.c_str()), 116*93e2932fSMatt Spinler entry("REASON_CODE=%s", rc.c_str())); 117*93e2932fSMatt Spinler 118*93e2932fSMatt Spinler throw std::runtime_error("Invalid reason code in message registry"); 119*93e2932fSMatt Spinler } 120*93e2932fSMatt Spinler return reasonCode; 121*93e2932fSMatt Spinler } 122*93e2932fSMatt Spinler 123*93e2932fSMatt Spinler uint8_t getSRCType(const nlohmann::json& src, const std::string& name) 124*93e2932fSMatt Spinler { 125*93e2932fSMatt Spinler // Looks like: "22" 126*93e2932fSMatt Spinler std::string srcType = src["Type"]; 127*93e2932fSMatt Spinler size_t type = strtoul(srcType.c_str(), nullptr, 16); 128*93e2932fSMatt Spinler if ((type == 0) || (srcType.size() != 2)) // 1 hex byte 129*93e2932fSMatt Spinler { 130*93e2932fSMatt Spinler log<phosphor::logging::level::ERR>( 131*93e2932fSMatt Spinler "Invalid SRC Type in message registry", 132*93e2932fSMatt Spinler entry("ERROR_NAME=%s", name.c_str()), 133*93e2932fSMatt Spinler entry("SRC_TYPE=%s", srcType.c_str())); 134*93e2932fSMatt Spinler 135*93e2932fSMatt Spinler throw std::runtime_error("Invalid SRC Type in message registry"); 136*93e2932fSMatt Spinler } 137*93e2932fSMatt Spinler 138*93e2932fSMatt Spinler return type; 139*93e2932fSMatt Spinler } 140*93e2932fSMatt Spinler 141*93e2932fSMatt Spinler std::optional<std::map<SRC::WordNum, SRC::AdditionalDataField>> 142*93e2932fSMatt Spinler getSRCHexwordFields(const nlohmann::json& src, const std::string& name) 143*93e2932fSMatt Spinler { 144*93e2932fSMatt Spinler std::map<SRC::WordNum, SRC::AdditionalDataField> hexwordFields; 145*93e2932fSMatt Spinler 146*93e2932fSMatt Spinler // Build the map of which AdditionalData fields to use for which SRC words 147*93e2932fSMatt Spinler 148*93e2932fSMatt Spinler // Like: 149*93e2932fSMatt Spinler // { 150*93e2932fSMatt Spinler // "8": 151*93e2932fSMatt Spinler // { 152*93e2932fSMatt Spinler // "AdditionalDataPropSource": "TEST" 153*93e2932fSMatt Spinler // } 154*93e2932fSMatt Spinler // 155*93e2932fSMatt Spinler // } 156*93e2932fSMatt Spinler 157*93e2932fSMatt Spinler for (const auto& word : src["Words6To9"].items()) 158*93e2932fSMatt Spinler { 159*93e2932fSMatt Spinler std::string num = word.key(); 160*93e2932fSMatt Spinler size_t wordNum = std::strtoul(num.c_str(), nullptr, 10); 161*93e2932fSMatt Spinler 162*93e2932fSMatt Spinler if (wordNum == 0) 163*93e2932fSMatt Spinler { 164*93e2932fSMatt Spinler log<phosphor::logging::level::ERR>( 165*93e2932fSMatt Spinler "Invalid SRC word number in message registry", 166*93e2932fSMatt Spinler entry("ERROR_NAME=%s", name.c_str()), 167*93e2932fSMatt Spinler entry("SRC_WORD_NUM=%s", num.c_str())); 168*93e2932fSMatt Spinler 169*93e2932fSMatt Spinler throw std::runtime_error("Invalid SRC word in message registry"); 170*93e2932fSMatt Spinler } 171*93e2932fSMatt Spinler 172*93e2932fSMatt Spinler auto attributes = word.value(); 173*93e2932fSMatt Spinler std::string adPropName = attributes["AdditionalDataPropSource"]; 174*93e2932fSMatt Spinler hexwordFields[wordNum] = std::move(adPropName); 175*93e2932fSMatt Spinler } 176*93e2932fSMatt Spinler 177*93e2932fSMatt Spinler if (!hexwordFields.empty()) 178*93e2932fSMatt Spinler { 179*93e2932fSMatt Spinler return hexwordFields; 180*93e2932fSMatt Spinler } 181*93e2932fSMatt Spinler 182*93e2932fSMatt Spinler return std::nullopt; 183*93e2932fSMatt Spinler } 184*93e2932fSMatt Spinler std::optional<std::vector<SRC::WordNum>> 185*93e2932fSMatt Spinler getSRCSymptomIDFields(const nlohmann::json& src, const std::string& name) 186*93e2932fSMatt Spinler { 187*93e2932fSMatt Spinler std::vector<SRC::WordNum> symptomIDFields; 188*93e2932fSMatt Spinler 189*93e2932fSMatt Spinler // Looks like: 190*93e2932fSMatt Spinler // "SymptomIDFields": ["SRCWord3", "SRCWord6"], 191*93e2932fSMatt Spinler 192*93e2932fSMatt Spinler for (const std::string& field : src["SymptomIDFields"]) 193*93e2932fSMatt Spinler { 194*93e2932fSMatt Spinler // Just need the last digit off the end, e.g. SRCWord6. 195*93e2932fSMatt Spinler // The schema enforces the format of these. 196*93e2932fSMatt Spinler auto srcWordNum = field.substr(field.size() - 1); 197*93e2932fSMatt Spinler size_t num = std::strtoul(srcWordNum.c_str(), nullptr, 10); 198*93e2932fSMatt Spinler if (num == 0) 199*93e2932fSMatt Spinler { 200*93e2932fSMatt Spinler log<phosphor::logging::level::ERR>( 201*93e2932fSMatt Spinler "Invalid symptom ID field in message registry", 202*93e2932fSMatt Spinler entry("ERROR_NAME=%s", name.c_str()), 203*93e2932fSMatt Spinler entry("FIELD_NAME=%s", srcWordNum.c_str())); 204*93e2932fSMatt Spinler 205*93e2932fSMatt Spinler throw std::runtime_error("Invalid symptom ID in message registry"); 206*93e2932fSMatt Spinler } 207*93e2932fSMatt Spinler symptomIDFields.push_back(num); 208*93e2932fSMatt Spinler } 209*93e2932fSMatt Spinler if (!symptomIDFields.empty()) 210*93e2932fSMatt Spinler { 211*93e2932fSMatt Spinler return symptomIDFields; 212*93e2932fSMatt Spinler } 213*93e2932fSMatt Spinler 214*93e2932fSMatt Spinler return std::nullopt; 215*93e2932fSMatt Spinler } 216*93e2932fSMatt Spinler 217*93e2932fSMatt Spinler uint16_t getComponentID(uint8_t srcType, uint16_t reasonCode, 218*93e2932fSMatt Spinler const nlohmann::json& pelEntry, const std::string& name) 219*93e2932fSMatt Spinler { 220*93e2932fSMatt Spinler uint16_t id = 0; 221*93e2932fSMatt Spinler 222*93e2932fSMatt Spinler // If the ComponentID field is there, use that. Otherwise, if it's a 223*93e2932fSMatt Spinler // 0xBD BMC error SRC, use the reasoncode. 224*93e2932fSMatt Spinler if (pelEntry.find("ComponentID") != pelEntry.end()) 225*93e2932fSMatt Spinler { 226*93e2932fSMatt Spinler std::string componentID = pelEntry["ComponentID"]; 227*93e2932fSMatt Spinler id = strtoul(componentID.c_str(), nullptr, 16); 228*93e2932fSMatt Spinler } 229*93e2932fSMatt Spinler else 230*93e2932fSMatt Spinler { 231*93e2932fSMatt Spinler // On BMC error SRCs (BD), can just get the component ID from 232*93e2932fSMatt Spinler // the first byte of the reason code. 233*93e2932fSMatt Spinler if (srcType == static_cast<uint8_t>(SRCType::bmcError)) 234*93e2932fSMatt Spinler { 235*93e2932fSMatt Spinler id = reasonCode & 0xFF00; 236*93e2932fSMatt Spinler } 237*93e2932fSMatt Spinler else 238*93e2932fSMatt Spinler { 239*93e2932fSMatt Spinler log<level::ERR>("Missing component ID field in message registry", 240*93e2932fSMatt Spinler entry("ERROR_NAME=%s", name.c_str())); 241*93e2932fSMatt Spinler 242*93e2932fSMatt Spinler throw std::runtime_error( 243*93e2932fSMatt Spinler "Missing component ID field in message registry"); 244*93e2932fSMatt Spinler } 245*93e2932fSMatt Spinler } 246*93e2932fSMatt Spinler 247*93e2932fSMatt Spinler return id; 248*93e2932fSMatt Spinler } 249*93e2932fSMatt Spinler 250367144cfSMatt Spinler } // namespace helper 251367144cfSMatt Spinler 252367144cfSMatt Spinler std::optional<Entry> Registry::lookup(const std::string& name) 253367144cfSMatt Spinler { 254367144cfSMatt Spinler // Look in /etc first in case someone put a test file there 255367144cfSMatt Spinler fs::path debugFile{fs::path{debugFilePath} / registryFileName}; 256367144cfSMatt Spinler nlohmann::json registry; 257367144cfSMatt Spinler std::ifstream file; 258367144cfSMatt Spinler 259367144cfSMatt Spinler if (fs::exists(debugFile)) 260367144cfSMatt Spinler { 261367144cfSMatt Spinler log<level::INFO>("Using debug PEL message registry"); 262367144cfSMatt Spinler file.open(debugFile); 263367144cfSMatt Spinler } 264367144cfSMatt Spinler else 265367144cfSMatt Spinler { 266367144cfSMatt Spinler file.open(_registryFile); 267367144cfSMatt Spinler } 268367144cfSMatt Spinler 269367144cfSMatt Spinler try 270367144cfSMatt Spinler { 271367144cfSMatt Spinler registry = nlohmann::json::parse(file); 272367144cfSMatt Spinler } 273367144cfSMatt Spinler catch (std::exception& e) 274367144cfSMatt Spinler { 275367144cfSMatt Spinler log<level::ERR>("Error parsing message registry JSON", 276367144cfSMatt Spinler entry("JSON_ERROR=%s", e.what())); 277367144cfSMatt Spinler return std::nullopt; 278367144cfSMatt Spinler } 279367144cfSMatt Spinler 280367144cfSMatt Spinler // Find an entry with this name in the PEL array. 281367144cfSMatt Spinler auto e = std::find_if(registry["PELs"].begin(), registry["PELs"].end(), 282367144cfSMatt Spinler [&name](const auto& j) { return name == j["Name"]; }); 283367144cfSMatt Spinler 284367144cfSMatt Spinler if (e != registry["PELs"].end()) 285367144cfSMatt Spinler { 286367144cfSMatt Spinler // Fill in the Entry structure from the JSON. Most, but not all, fields 287367144cfSMatt Spinler // are optional. 288367144cfSMatt Spinler 289367144cfSMatt Spinler try 290367144cfSMatt Spinler { 291367144cfSMatt Spinler Entry entry; 292367144cfSMatt Spinler entry.name = (*e)["Name"]; 293367144cfSMatt Spinler entry.subsystem = helper::getSubsystem((*e)["Subsystem"]); 294367144cfSMatt Spinler entry.actionFlags = helper::getActionFlags((*e)["ActionFlags"]); 295367144cfSMatt Spinler 296367144cfSMatt Spinler if (e->find("MfgActionFlags") != e->end()) 297367144cfSMatt Spinler { 298367144cfSMatt Spinler entry.mfgActionFlags = 299367144cfSMatt Spinler helper::getActionFlags((*e)["MfgActionFlags"]); 300367144cfSMatt Spinler } 301367144cfSMatt Spinler 302367144cfSMatt Spinler if (e->find("Severity") != e->end()) 303367144cfSMatt Spinler { 304367144cfSMatt Spinler entry.severity = helper::getSeverity((*e)["Severity"]); 305367144cfSMatt Spinler } 306367144cfSMatt Spinler 307367144cfSMatt Spinler if (e->find("MfgSeverity") != e->end()) 308367144cfSMatt Spinler { 309367144cfSMatt Spinler entry.mfgSeverity = helper::getSeverity((*e)["MfgSeverity"]); 310367144cfSMatt Spinler } 311367144cfSMatt Spinler 312367144cfSMatt Spinler if (e->find("EventType") != e->end()) 313367144cfSMatt Spinler { 314367144cfSMatt Spinler entry.eventType = helper::getEventType((*e)["EventType"]); 315367144cfSMatt Spinler } 316367144cfSMatt Spinler 317367144cfSMatt Spinler if (e->find("EventScope") != e->end()) 318367144cfSMatt Spinler { 319367144cfSMatt Spinler entry.eventScope = helper::getEventScope((*e)["EventScope"]); 320367144cfSMatt Spinler } 321367144cfSMatt Spinler 322*93e2932fSMatt Spinler auto& src = (*e)["SRC"]; 323*93e2932fSMatt Spinler entry.src.reasonCode = helper::getSRCReasonCode(src, name); 324*93e2932fSMatt Spinler 325*93e2932fSMatt Spinler if (src.find("Type") != src.end()) 326*93e2932fSMatt Spinler { 327*93e2932fSMatt Spinler entry.src.type = helper::getSRCType(src, name); 328*93e2932fSMatt Spinler } 329*93e2932fSMatt Spinler else 330*93e2932fSMatt Spinler { 331*93e2932fSMatt Spinler entry.src.type = static_cast<uint8_t>(SRCType::bmcError); 332*93e2932fSMatt Spinler } 333*93e2932fSMatt Spinler 334*93e2932fSMatt Spinler // Now that we know the SRC type and reason code, 335*93e2932fSMatt Spinler // we can get the component ID. 336*93e2932fSMatt Spinler entry.componentID = helper::getComponentID( 337*93e2932fSMatt Spinler entry.src.type, entry.src.reasonCode, *e, name); 338*93e2932fSMatt Spinler 339*93e2932fSMatt Spinler if (src.find("Words6To9") != src.end()) 340*93e2932fSMatt Spinler { 341*93e2932fSMatt Spinler entry.src.hexwordADFields = 342*93e2932fSMatt Spinler helper::getSRCHexwordFields(src, name); 343*93e2932fSMatt Spinler } 344*93e2932fSMatt Spinler 345*93e2932fSMatt Spinler if (src.find("SymptomIDFields") != src.end()) 346*93e2932fSMatt Spinler { 347*93e2932fSMatt Spinler entry.src.symptomID = helper::getSRCSymptomIDFields(src, name); 348*93e2932fSMatt Spinler } 349*93e2932fSMatt Spinler 350*93e2932fSMatt Spinler if (src.find("PowerFault") != src.end()) 351*93e2932fSMatt Spinler { 352*93e2932fSMatt Spinler entry.src.powerFault = src["PowerFault"]; 353*93e2932fSMatt Spinler } 354367144cfSMatt Spinler 355367144cfSMatt Spinler return entry; 356367144cfSMatt Spinler } 357367144cfSMatt Spinler catch (std::exception& e) 358367144cfSMatt Spinler { 359367144cfSMatt Spinler log<level::ERR>("Found invalid message registry field", 360367144cfSMatt Spinler entry("ERROR=%s", e.what())); 361367144cfSMatt Spinler } 362367144cfSMatt Spinler } 363367144cfSMatt Spinler 364367144cfSMatt Spinler return std::nullopt; 365367144cfSMatt Spinler } 366367144cfSMatt Spinler 367367144cfSMatt Spinler } // namespace message 368367144cfSMatt Spinler } // namespace pels 369367144cfSMatt Spinler } // namespace openpower 370