1 #pragma once
2 #include "additional_data.hpp"
3 
4 #include <nlohmann/json.hpp>
5 
6 #include <filesystem>
7 #include <optional>
8 #include <string>
9 #include <vector>
10 
11 namespace openpower
12 {
13 namespace pels
14 {
15 namespace message
16 {
17 
18 constexpr auto registryFileName = "message_registry.json";
19 enum class LookupType
20 {
21     name = 0,
22     reasonCode = 1
23 };
24 
25 /**
26  * @brief A possible severity/system type combination
27  *
28  * If there is no system type defined for this entry,
29  * then the system field will be empty.
30  */
31 struct RegistrySeverity
32 {
33     std::string system;
34     uint8_t severity;
35 };
36 
37 /**
38  * @brief Represents the Documentation related fields in the message registry.
39  *        It is part of the 'Entry' structure that will be filled in when
40  *        an error is looked up in the registry.
41  *
42  * If a field is wrapped by std::optional, it means the field is
43  * optional in the JSON and higher level code knows how to handle it.
44  */
45 struct DOC
46 {
47     /**
48      * @brief Description of error
49      */
50     std::string description;
51 
52     /**
53      * @brief Error message field
54      */
55     std::string message;
56 
57     /**
58      * @brief An optional vector of SRC word 6-9 to use as the source of the
59      *        numeric arguments that will be substituted into any placeholder
60      *        in the Message field.
61      */
62     std::optional<std::vector<std::string>> messageArgSources;
63 };
64 
65 /**
66  * @brief Represents the SRC related fields in the message registry.
67  *        It is part of the 'Entry' structure that will be filled in when
68  *        an error is looked up in the registry.
69  *
70  * If a field is wrapped by std::optional, it means the field is
71  * optional in the JSON and higher level code knows how to handle it.
72  */
73 struct SRC
74 {
75     /**
76      * @brief SRC type - The first byte of the ASCII string
77      */
78     uint8_t type;
79 
80     /**
81      * @brief The SRC reason code (2nd half of 4B 'ASCII string' word)
82      */
83     uint16_t reasonCode;
84 
85     /**
86      * @brief An optional vector of SRC hexword numbers that should be used
87      *        along with the SRC ASCII string to build the Symptom ID, which
88      *        is a field in the Extended Header section.
89      */
90     using WordNum = size_t;
91     std::optional<std::vector<WordNum>> symptomID;
92 
93     /**
94      * @brief Which AdditionalData fields to use to fill in the user defined
95      *        SRC hexwords.
96      *
97      * For example, if the AdditionalData event log property contained
98      * "CHIPNUM=42" and this map contained {6, {"CHIPNUM", "DESC"}}, then the
99      * code would put 42 into SRC hexword 6.
100      *
101      * AdditionalDataField specifies two fields from the SRC entry in the
102      * message registry: "AdditionalDataPropSource" and "Description"
103      */
104     using AdditionalDataField = std::tuple<std::string, std::string>;
105     std::optional<std::map<WordNum, AdditionalDataField>> hexwordADFields;
106 
107     SRC() : type(0), reasonCode(0) {}
108 };
109 
110 /**
111  * @brief Represents a message registry entry, which is used for creating a
112  *        PEL from an OpenBMC event log.
113  */
114 struct Entry
115 {
116     /**
117      * @brief The error name, like "xyz.openbmc_project.Error.Foo".
118      */
119     std::string name;
120 
121     /**
122      * @brief The component ID of the PEL creator.
123      */
124     uint16_t componentID;
125 
126     /**
127      * @brief The PEL subsystem field.
128      */
129     std::optional<uint8_t> subsystem;
130 
131     /**
132      * @brief The optional PEL severity field.  If not specified, the PEL
133      *        will use the severity of the OpenBMC event log.
134      *
135      * If the system type is specified in any of the entries in the vector,
136      * then the system type will be needed to find the actual severity.
137      */
138     std::optional<std::vector<RegistrySeverity>> severity;
139 
140     /**
141      * @brief The optional severity field to use when in manufacturing tolerance
142      *        mode.  It behaves like the severity field above.
143      */
144     std::optional<std::vector<RegistrySeverity>> mfgSeverity;
145 
146     /**
147      * @brief The PEL action flags field.
148      */
149     std::optional<uint16_t> actionFlags;
150 
151     /**
152      * @brief  The optional action flags to use instead when in manufacturing
153      * tolerance mode.
154      */
155     std::optional<uint16_t> mfgActionFlags;
156 
157     /**
158      * @brief The PEL event type field.  If not specified, higher level code
159      *        will decide the value.
160      */
161     std::optional<uint8_t> eventType;
162 
163     /**
164      * @brief The PEL event scope field.  If not specified, higher level code
165      *        will decide the value.
166      */
167     std::optional<uint8_t> eventScope;
168 
169     /**
170      * The SRC related fields.
171      */
172     SRC src;
173 
174     /**
175      * The Documentation related fields.
176      */
177     DOC doc;
178 
179     /**
180      * @brief The callout JSON, if the entry has callouts.
181      */
182     std::optional<nlohmann::json> callouts;
183 };
184 
185 /**
186  * @brief Holds callout information pulled out of the JSON.
187  */
188 struct RegistryCallout
189 {
190     std::string priority;
191     std::string locCode;
192     std::string procedure;
193     std::string symbolicFRU;
194     std::string symbolicFRUTrusted;
195     bool useInventoryLocCode;
196 };
197 
198 /**
199  * @class Registry
200  *
201  * This class wraps the message registry JSON data and allows one to find
202  * the message registry entry pertaining to the error name.
203  *
204  * So that new registry files can easily be tested, the code will look for
205  * /etc/phosphor-logging/message_registry.json before looking for the real
206  * path.
207  */
208 class Registry
209 {
210   public:
211     Registry() = delete;
212     ~Registry() = default;
213     Registry(const Registry&) = default;
214     Registry& operator=(const Registry&) = default;
215     Registry(Registry&&) = default;
216     Registry& operator=(Registry&&) = default;
217 
218     /**
219      * @brief Constructor
220      *
221      * Will load the callout JSON.
222      *
223      * @param[in] registryFile - The path to the file.
224      */
225     explicit Registry(const std::filesystem::path& registryFile) :
226         Registry(registryFile, true)
227     {}
228 
229     /**
230      * @brief Constructor
231      *
232      * This version contains a parameter that allows the callout JSON
233      * to be saved in the Entry struct or not, as it isn't needed at
234      * all in some cases.
235      *
236      * @param[in] registryFile - The path to the file.
237      * @param[in] loadCallouts - If the callout JSON should be saved.
238      */
239     explicit Registry(const std::filesystem::path& registryFile,
240                       bool loadCallouts) :
241         _registryFile(registryFile),
242         _loadCallouts(loadCallouts)
243     {}
244 
245     /**
246      * @brief Find a registry entry based on its error name or reason code.
247      *
248      * This function does do some basic sanity checking on the JSON contents,
249      * but there is also an external program that enforces a schema on the
250      * registry JSON that should catch all of these problems ahead of time.
251      *
252      * @param[in] name - The error name, like xyz.openbmc_project.Error.Foo
253      *                 - OR
254      *                 - The reason code, like 0x1001
255      * @param[in] type - LookupType enum value
256      * @param[in] toCache - boolean to cache registry in memory
257      * @return optional<Entry> A filled in message registry structure if
258      *                         found, otherwise an empty optional object.
259      */
260     std::optional<Entry> lookup(const std::string& name, LookupType type,
261                                 bool toCache = false);
262 
263     /**
264      * @brief Find the callouts to put into the PEL based on the calloutJSON
265      *        data.
266      *
267      * The system type and AdditionalData are used to index into the correct
268      * callout table.
269      *
270      * Throws exceptions on failures.
271      *
272      * @param[in] calloutJSON - Where to look up the  callouts
273      * @param[in] systemNames - List of compatible system type names
274      * @param[in] additionalData - The AdditionalData property
275      *
276      * @return std::vector<RegistryCallout> - The callouts to use
277      */
278     static std::vector<RegistryCallout>
279         getCallouts(const nlohmann::json& calloutJSON,
280                     const std::vector<std::string>& systemNames,
281                     const AdditionalData& additionalData);
282 
283   private:
284     /**
285      * @brief Parse message registry file using nlohmann::json
286      * @param[in] registryFile - The message registry JSON file
287      * @return optional<nlohmann::json> The full message registry object or an
288      *                                  empty optional object upon failure.
289      */
290     std::optional<nlohmann::json>
291         readRegistry(const std::filesystem::path& registryFile);
292 
293     /**
294      * @brief The path to the registry JSON file.
295      */
296     std::filesystem::path _registryFile;
297 
298     /**
299      * @brief The full message registry object.
300      */
301     std::optional<nlohmann::json> _registry;
302 
303     /**
304      * @brief If the callout JSON should be saved in the Entry on lookup.
305      */
306     bool _loadCallouts;
307 };
308 
309 namespace helper
310 {
311 
312 /**
313  * @brief A helper function to get the PEL subsystem value based on
314  *        the registry subsystem name.
315  *
316  * @param[in] subsystemName - The registry name for the subsystem
317  *
318  * @return uint8_t The PEL subsystem value
319  */
320 uint8_t getSubsystem(const std::string& subsystemName);
321 
322 /**
323  * @brief A helper function to get the PEL severity value based on
324  *        the registry severity name.
325  *
326  * @param[in] severityName - The registry name for the severity
327  *
328  * @return uint8_t The PEL severity value
329  */
330 uint8_t getSeverity(const std::string& severityName);
331 
332 /**
333  * @brief Returns all of the system type/severity values found
334  * in the severity JSON passed in.
335  *
336  * The JSON is either a simple string, like:
337  *     "unrecoverable"
338  * or an array of system type/severity pairs, like:
339  *     [
340  *        {
341  *            "System": "1",
342  *            "SevValue": "predictive"
343  *        },
344  *        {
345  *            "System": "2",
346  *            "SevValue": "recovered"
347  *        }
348  *     ]
349  *
350  * @param[in] severity - The severity JSON
351  * @return The list of severity/system combinations.  If the System key
352  *         wasn't used, then that field will be empty in the structure.
353  */
354 std::vector<RegistrySeverity> getSeverities(const nlohmann::json& severity);
355 
356 /**
357  * @brief A helper function to get the action flags value based on
358  *        the action flag names used in the registry.
359  *
360  * @param[in] flags - The list of flag names from the registry.
361  *
362  * @return uint16_t - The bitfield of flags used in the PEL.
363  */
364 uint16_t getActionFlags(const std::vector<std::string>& flags);
365 
366 /**
367  * @brief A helper function to get the PEL event type value based on
368  *        the registry event type name.
369  *
370  * @param[in] eventTypeName - The registry name for the event type
371  *
372  * @return uint8_t The PEL event type value
373  */
374 uint8_t getEventType(const std::string& eventTypeName);
375 
376 /**
377  * @brief A helper function to get the PEL event scope value based on
378  *        the registry event scope name.
379  *
380  * @param[in] eventScopeName - The registry name for the event scope
381  *
382  * @return uint8_t The PEL event scope value
383  */
384 uint8_t getEventScope(const std::string& eventScopeName);
385 
386 /**
387  * @brief Reads the "ReasonCode" field out of JSON and converts the string value
388  *        such as "0x5555" to a uint16 like 0x5555.
389  *
390  * @param[in] src - The message registry SRC dictionary to read from
391  * @param[in] name - The error name, to use in a trace if things go awry.
392  *
393  * @return uint16_t - The reason code
394  */
395 uint16_t getSRCReasonCode(const nlohmann::json& src, const std::string& name);
396 
397 /**
398  * @brief Reads the "Type" field out of JSON and converts it to the SRC::Type
399  *        value.
400  *
401  * @param[in] src - The message registry SRC dictionary to read from
402  * @param[in] name - The error name, to use in a trace if things go awry.
403  *
404  * @return uint8_t - The SRC type value, like 0x11
405  */
406 uint8_t getSRCType(const nlohmann::json& src, const std::string& name);
407 
408 /**
409  * @brief Reads the "Words6To9" field out of JSON and converts it to a map
410  *        of the SRC word number to the AdditionalData property field used
411  *        to fill it in with.
412  *
413  * @param[in] src - The message registry SRC dictionary to read from
414  * @param[in] name - The error name, to use in a trace if things go awry.
415  *
416  * @return std::optional<std::map<SRC::WordNum, SRC::AdditionalDataField>>
417  */
418 std::optional<std::map<SRC::WordNum, SRC::AdditionalDataField>>
419     getSRCHexwordFields(const nlohmann::json& src, const std::string& name);
420 
421 /**
422  * @brief Reads the "SymptomIDFields" field out of JSON and converts it to
423  *        a vector of SRC word numbers.
424  *
425  * @param[in] src - The message registry SRC dictionary to read from
426  * @param[in] name - The error name, to use in a trace if things go awry.
427  *
428  * @return std::optional<std::vector<SRC::WordNum>>
429  */
430 std::optional<std::vector<SRC::WordNum>>
431     getSRCSymptomIDFields(const nlohmann::json& src, const std::string& name);
432 
433 /**
434  * @brief Reads the "ComponentID" field out of JSON and converts it to a
435  *        uint16_t like 0xFF00.
436  *
437  * The ComponentID JSON field is only required if the SRC type isn't a BD
438  * BMC SRC, because for those SRCs it can be inferred from the upper byte
439  * of the SRC reasoncode.
440  *
441  * @param[in] srcType - The SRC type
442  * @param[in] reasonCode - The SRC reason code
443  * @param[in] pelEntry - The PEL entry JSON
444  * @param[in] name - The error name, to use in a trace if things go awry.
445  *
446  * @return uin16_t - The component ID, like 0xFF00
447  */
448 uint16_t getComponentID(uint8_t srcType, uint16_t reasonCode,
449                         const nlohmann::json& pelEntry,
450                         const std::string& name);
451 
452 } // namespace helper
453 
454 } // namespace message
455 
456 } // namespace pels
457 } // namespace openpower
458