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