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