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