blob: f0efb3e90453993b9ed457e788fc6d87016122d6 [file] [log] [blame]
Matt Spinler367144c2019-09-19 15:33:52 -05001#include "registry.hpp"
2
3#include "pel_types.hpp"
4#include "pel_values.hpp"
5
6#include <fstream>
7#include <phosphor-logging/log.hpp>
8
9namespace openpower
10{
11namespace pels
12{
13namespace message
14{
15
16namespace pv = pel_values;
17namespace fs = std::filesystem;
18using namespace phosphor::logging;
19
20constexpr auto debugFilePath = "/etc/phosphor-logging/";
21
22namespace helper
23{
24
25uint8_t getSubsystem(const std::string& subsystemName)
26{
27 // Get the actual value to use in the PEL for the string name
28 auto ss = pv::findByName(subsystemName, pv::subsystemValues);
29 if (ss == pv::subsystemValues.end())
30 {
31 // Schema validation should be catching this.
32 log<level::ERR>("Invalid subsystem name used in message registry",
33 entry("SUBSYSTEM=%s", subsystemName.c_str()));
34
35 throw std::runtime_error("Invalid subsystem used in message registry");
36 }
37
38 return std::get<pv::fieldValuePos>(*ss);
39}
40
41uint8_t getSeverity(const std::string& severityName)
42{
43 auto s = pv::findByName(severityName, pv::severityValues);
44 if (s == pv::severityValues.end())
45 {
46 // Schema validation should be catching this.
47 log<level::ERR>("Invalid severity name used in message registry",
48 entry("SEVERITY=%s", severityName.c_str()));
49
50 throw std::runtime_error("Invalid severity used in message registry");
51 }
52
53 return std::get<pv::fieldValuePos>(*s);
54}
55
56uint16_t getActionFlags(const std::vector<std::string>& flags)
57{
58 uint16_t actionFlags = 0;
59
60 // Make the bitmask based on the array of flag names
61 for (const auto& flag : flags)
62 {
63 auto s = pv::findByName(flag, pv::actionFlagsValues);
64 if (s == pv::actionFlagsValues.end())
65 {
66 // Schema validation should be catching this.
67 log<level::ERR>("Invalid action flag name used in message registry",
68 entry("FLAG=%s", flag.c_str()));
69
70 throw std::runtime_error(
71 "Invalid action flag used in message registry");
72 }
73
74 actionFlags |= std::get<pv::fieldValuePos>(*s);
75 }
76
77 return actionFlags;
78}
79
80uint8_t getEventType(const std::string& eventTypeName)
81{
82 auto t = pv::findByName(eventTypeName, pv::eventTypeValues);
83 if (t == pv::eventTypeValues.end())
84 {
85 log<level::ERR>("Invalid event type used in message registry",
86 entry("EVENT_TYPE=%s", eventTypeName.c_str()));
87
88 throw std::runtime_error("Invalid event type used in message registry");
89 }
90 return std::get<pv::fieldValuePos>(*t);
91}
92
93uint8_t getEventScope(const std::string& eventScopeName)
94{
95 auto s = pv::findByName(eventScopeName, pv::eventScopeValues);
96 if (s == pv::eventScopeValues.end())
97 {
98 log<level::ERR>("Invalid event scope used in registry",
99 entry("EVENT_SCOPE=%s", eventScopeName.c_str()));
100
101 throw std::runtime_error(
102 "Invalid event scope used in message registry");
103 }
104 return std::get<pv::fieldValuePos>(*s);
105}
106
Matt Spinler93e29322019-09-20 11:16:15 -0500107uint16_t getSRCReasonCode(const nlohmann::json& src, const std::string& name)
108{
109 std::string rc = src["ReasonCode"];
110 uint16_t reasonCode = strtoul(rc.c_str(), nullptr, 16);
111 if (reasonCode == 0)
112 {
113 log<phosphor::logging::level::ERR>(
114 "Invalid reason code in message registry",
115 entry("ERROR_NAME=%s", name.c_str()),
116 entry("REASON_CODE=%s", rc.c_str()));
117
118 throw std::runtime_error("Invalid reason code in message registry");
119 }
120 return reasonCode;
121}
122
123uint8_t getSRCType(const nlohmann::json& src, const std::string& name)
124{
125 // Looks like: "22"
126 std::string srcType = src["Type"];
127 size_t type = strtoul(srcType.c_str(), nullptr, 16);
128 if ((type == 0) || (srcType.size() != 2)) // 1 hex byte
129 {
130 log<phosphor::logging::level::ERR>(
131 "Invalid SRC Type in message registry",
132 entry("ERROR_NAME=%s", name.c_str()),
133 entry("SRC_TYPE=%s", srcType.c_str()));
134
135 throw std::runtime_error("Invalid SRC Type in message registry");
136 }
137
138 return type;
139}
140
141std::optional<std::map<SRC::WordNum, SRC::AdditionalDataField>>
142 getSRCHexwordFields(const nlohmann::json& src, const std::string& name)
143{
144 std::map<SRC::WordNum, SRC::AdditionalDataField> hexwordFields;
145
146 // Build the map of which AdditionalData fields to use for which SRC words
147
148 // Like:
149 // {
150 // "8":
151 // {
152 // "AdditionalDataPropSource": "TEST"
153 // }
154 //
155 // }
156
157 for (const auto& word : src["Words6To9"].items())
158 {
159 std::string num = word.key();
160 size_t wordNum = std::strtoul(num.c_str(), nullptr, 10);
161
162 if (wordNum == 0)
163 {
164 log<phosphor::logging::level::ERR>(
165 "Invalid SRC word number in message registry",
166 entry("ERROR_NAME=%s", name.c_str()),
167 entry("SRC_WORD_NUM=%s", num.c_str()));
168
169 throw std::runtime_error("Invalid SRC word in message registry");
170 }
171
172 auto attributes = word.value();
173 std::string adPropName = attributes["AdditionalDataPropSource"];
174 hexwordFields[wordNum] = std::move(adPropName);
175 }
176
177 if (!hexwordFields.empty())
178 {
179 return hexwordFields;
180 }
181
182 return std::nullopt;
183}
184std::optional<std::vector<SRC::WordNum>>
185 getSRCSymptomIDFields(const nlohmann::json& src, const std::string& name)
186{
187 std::vector<SRC::WordNum> symptomIDFields;
188
189 // Looks like:
190 // "SymptomIDFields": ["SRCWord3", "SRCWord6"],
191
192 for (const std::string& field : src["SymptomIDFields"])
193 {
194 // Just need the last digit off the end, e.g. SRCWord6.
195 // The schema enforces the format of these.
196 auto srcWordNum = field.substr(field.size() - 1);
197 size_t num = std::strtoul(srcWordNum.c_str(), nullptr, 10);
198 if (num == 0)
199 {
200 log<phosphor::logging::level::ERR>(
201 "Invalid symptom ID field in message registry",
202 entry("ERROR_NAME=%s", name.c_str()),
203 entry("FIELD_NAME=%s", srcWordNum.c_str()));
204
205 throw std::runtime_error("Invalid symptom ID in message registry");
206 }
207 symptomIDFields.push_back(num);
208 }
209 if (!symptomIDFields.empty())
210 {
211 return symptomIDFields;
212 }
213
214 return std::nullopt;
215}
216
217uint16_t getComponentID(uint8_t srcType, uint16_t reasonCode,
218 const nlohmann::json& pelEntry, const std::string& name)
219{
220 uint16_t id = 0;
221
222 // If the ComponentID field is there, use that. Otherwise, if it's a
223 // 0xBD BMC error SRC, use the reasoncode.
224 if (pelEntry.find("ComponentID") != pelEntry.end())
225 {
226 std::string componentID = pelEntry["ComponentID"];
227 id = strtoul(componentID.c_str(), nullptr, 16);
228 }
229 else
230 {
231 // On BMC error SRCs (BD), can just get the component ID from
232 // the first byte of the reason code.
233 if (srcType == static_cast<uint8_t>(SRCType::bmcError))
234 {
235 id = reasonCode & 0xFF00;
236 }
237 else
238 {
239 log<level::ERR>("Missing component ID field in message registry",
240 entry("ERROR_NAME=%s", name.c_str()));
241
242 throw std::runtime_error(
243 "Missing component ID field in message registry");
244 }
245 }
246
247 return id;
248}
249
Matt Spinler367144c2019-09-19 15:33:52 -0500250} // namespace helper
251
252std::optional<Entry> Registry::lookup(const std::string& name)
253{
254 // Look in /etc first in case someone put a test file there
255 fs::path debugFile{fs::path{debugFilePath} / registryFileName};
256 nlohmann::json registry;
257 std::ifstream file;
258
259 if (fs::exists(debugFile))
260 {
261 log<level::INFO>("Using debug PEL message registry");
262 file.open(debugFile);
263 }
264 else
265 {
266 file.open(_registryFile);
267 }
268
269 try
270 {
271 registry = nlohmann::json::parse(file);
272 }
273 catch (std::exception& e)
274 {
275 log<level::ERR>("Error parsing message registry JSON",
276 entry("JSON_ERROR=%s", e.what()));
277 return std::nullopt;
278 }
279
280 // Find an entry with this name in the PEL array.
281 auto e = std::find_if(registry["PELs"].begin(), registry["PELs"].end(),
282 [&name](const auto& j) { return name == j["Name"]; });
283
284 if (e != registry["PELs"].end())
285 {
286 // Fill in the Entry structure from the JSON. Most, but not all, fields
287 // are optional.
288
289 try
290 {
291 Entry entry;
292 entry.name = (*e)["Name"];
293 entry.subsystem = helper::getSubsystem((*e)["Subsystem"]);
294 entry.actionFlags = helper::getActionFlags((*e)["ActionFlags"]);
295
296 if (e->find("MfgActionFlags") != e->end())
297 {
298 entry.mfgActionFlags =
299 helper::getActionFlags((*e)["MfgActionFlags"]);
300 }
301
302 if (e->find("Severity") != e->end())
303 {
304 entry.severity = helper::getSeverity((*e)["Severity"]);
305 }
306
307 if (e->find("MfgSeverity") != e->end())
308 {
309 entry.mfgSeverity = helper::getSeverity((*e)["MfgSeverity"]);
310 }
311
312 if (e->find("EventType") != e->end())
313 {
314 entry.eventType = helper::getEventType((*e)["EventType"]);
315 }
316
317 if (e->find("EventScope") != e->end())
318 {
319 entry.eventScope = helper::getEventScope((*e)["EventScope"]);
320 }
321
Matt Spinler93e29322019-09-20 11:16:15 -0500322 auto& src = (*e)["SRC"];
323 entry.src.reasonCode = helper::getSRCReasonCode(src, name);
324
325 if (src.find("Type") != src.end())
326 {
327 entry.src.type = helper::getSRCType(src, name);
328 }
329 else
330 {
331 entry.src.type = static_cast<uint8_t>(SRCType::bmcError);
332 }
333
334 // Now that we know the SRC type and reason code,
335 // we can get the component ID.
336 entry.componentID = helper::getComponentID(
337 entry.src.type, entry.src.reasonCode, *e, name);
338
339 if (src.find("Words6To9") != src.end())
340 {
341 entry.src.hexwordADFields =
342 helper::getSRCHexwordFields(src, name);
343 }
344
345 if (src.find("SymptomIDFields") != src.end())
346 {
347 entry.src.symptomID = helper::getSRCSymptomIDFields(src, name);
348 }
349
350 if (src.find("PowerFault") != src.end())
351 {
352 entry.src.powerFault = src["PowerFault"];
353 }
Matt Spinler367144c2019-09-19 15:33:52 -0500354
355 return entry;
356 }
357 catch (std::exception& e)
358 {
359 log<level::ERR>("Found invalid message registry field",
360 entry("ERROR=%s", e.what()));
361 }
362 }
363
364 return std::nullopt;
365}
366
367} // namespace message
368} // namespace pels
369} // namespace openpower