blob: 3c4e01691a4c3dee5c1c22694949ad5e84d31230 [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
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800267std::optional<Entry> Registry::lookup(const std::string& name, LookupType type,
268 bool toCache)
Matt Spinler367144c2019-09-19 15:33:52 -0500269{
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800270 std::optional<nlohmann::json> registryTmp;
271 auto& registryOpt = (_registry) ? _registry : registryTmp;
272 if (!registryOpt)
Matt Spinler367144c2019-09-19 15:33:52 -0500273 {
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800274 registryOpt = readRegistry(_registryFile);
275 if (!registryOpt)
276 {
277 return std::nullopt;
278 }
279 else if (toCache)
280 {
281 // Save message registry in memory for peltool
282 _registry = std::move(registryTmp);
283 }
Matt Spinler367144c2019-09-19 15:33:52 -0500284 }
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800285 auto& reg = (_registry) ? _registry : registryTmp;
286 const auto& registry = reg.value();
Matt Spinler367144c2019-09-19 15:33:52 -0500287 // Find an entry with this name in the PEL array.
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800288 auto e = std::find_if(
289 registry["PELs"].begin(), registry["PELs"].end(),
290 [&name, &type](const auto& j) {
291 return ((name == j["Name"] && type == LookupType::name) ||
292 (name == j["SRC"]["ReasonCode"] &&
293 type == LookupType::reasonCode));
294 });
Matt Spinler367144c2019-09-19 15:33:52 -0500295
296 if (e != registry["PELs"].end())
297 {
298 // Fill in the Entry structure from the JSON. Most, but not all, fields
299 // are optional.
300
301 try
302 {
303 Entry entry;
304 entry.name = (*e)["Name"];
305 entry.subsystem = helper::getSubsystem((*e)["Subsystem"]);
Matt Spinlere07f9152019-11-01 10:48:36 -0500306
307 if (e->find("ActionFlags") != e->end())
308 {
309 entry.actionFlags = helper::getActionFlags((*e)["ActionFlags"]);
310 }
Matt Spinler367144c2019-09-19 15:33:52 -0500311
312 if (e->find("MfgActionFlags") != e->end())
313 {
314 entry.mfgActionFlags =
315 helper::getActionFlags((*e)["MfgActionFlags"]);
316 }
317
318 if (e->find("Severity") != e->end())
319 {
320 entry.severity = helper::getSeverity((*e)["Severity"]);
321 }
322
323 if (e->find("MfgSeverity") != e->end())
324 {
325 entry.mfgSeverity = helper::getSeverity((*e)["MfgSeverity"]);
326 }
327
328 if (e->find("EventType") != e->end())
329 {
330 entry.eventType = helper::getEventType((*e)["EventType"]);
331 }
332
333 if (e->find("EventScope") != e->end())
334 {
335 entry.eventScope = helper::getEventScope((*e)["EventScope"]);
336 }
337
Matt Spinler93e29322019-09-20 11:16:15 -0500338 auto& src = (*e)["SRC"];
339 entry.src.reasonCode = helper::getSRCReasonCode(src, name);
340
341 if (src.find("Type") != src.end())
342 {
343 entry.src.type = helper::getSRCType(src, name);
344 }
345 else
346 {
347 entry.src.type = static_cast<uint8_t>(SRCType::bmcError);
348 }
349
350 // Now that we know the SRC type and reason code,
351 // we can get the component ID.
352 entry.componentID = helper::getComponentID(
353 entry.src.type, entry.src.reasonCode, *e, name);
354
355 if (src.find("Words6To9") != src.end())
356 {
357 entry.src.hexwordADFields =
358 helper::getSRCHexwordFields(src, name);
359 }
360
361 if (src.find("SymptomIDFields") != src.end())
362 {
363 entry.src.symptomID = helper::getSRCSymptomIDFields(src, name);
364 }
365
366 if (src.find("PowerFault") != src.end())
367 {
368 entry.src.powerFault = src["PowerFault"];
369 }
Matt Spinler367144c2019-09-19 15:33:52 -0500370
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800371 auto& doc = (*e)["Documentation"];
372 entry.doc.message = doc["Message"];
373 entry.doc.description = doc["Description"];
374 if (doc.find("MessageArgSources") != doc.end())
375 {
376 entry.doc.messageArgSources = doc["MessageArgSources"];
377 }
378
Matt Spinler367144c2019-09-19 15:33:52 -0500379 return entry;
380 }
381 catch (std::exception& e)
382 {
383 log<level::ERR>("Found invalid message registry field",
384 entry("ERROR=%s", e.what()));
385 }
386 }
387
388 return std::nullopt;
389}
390
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800391std::optional<nlohmann::json>
392 Registry::readRegistry(const std::filesystem::path& registryFile)
393{
394 // Look in /etc first in case someone put a test file there
395 fs::path debugFile{fs::path{debugFilePath} / registryFileName};
396 nlohmann::json registry;
397 std::ifstream file;
398
399 if (fs::exists(debugFile))
400 {
401 log<level::INFO>("Using debug PEL message registry");
402 file.open(debugFile);
403 }
404 else
405 {
406 file.open(registryFile);
407 }
408
409 try
410 {
411 registry = nlohmann::json::parse(file);
412 }
413 catch (std::exception& e)
414 {
415 log<level::ERR>("Error parsing message registry JSON",
416 entry("JSON_ERROR=%s", e.what()));
417 return std::nullopt;
418 }
419 return registry;
420}
421
Matt Spinler367144c2019-09-19 15:33:52 -0500422} // namespace message
423} // namespace pels
424} // namespace openpower