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