xref: /openbmc/phosphor-logging/extensions/openpower-pels/registry.cpp (revision 93e2932f0667d54926e933eedab7ee2adc8b2731)
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