1 #pragma once
2 #include <filesystem>
3 #include <nlohmann/json.hpp>
4 #include <optional>
5 #include <string>
6 #include <vector>
7 
8 namespace openpower
9 {
10 namespace pels
11 {
12 namespace message
13 {
14 
15 constexpr auto registryFileName = "message_registry.json";
16 enum class LookupType
17 {
18     name = 0,
19     reasonCode = 1
20 };
21 
22 /**
23  * @brief Represents the Documentation related fields in the message registry.
24  *        It is part of the 'Entry' structure that will be filled in when
25  *        an error is looked up in the registry.
26  *
27  * If a field is wrapped by std::optional, it means the field is
28  * optional in the JSON and higher level code knows how to handle it.
29  */
30 struct DOC
31 {
32     /**
33      * @brief Description of error
34      */
35     std::string description;
36 
37     /**
38      * @brief Error message field
39      */
40     std::string message;
41 
42     /**
43      * @brief An optional vector of SRC word 6-9 to use as the source of the
44      *        numeric arguments that will be substituted into any placeholder
45      *        in the Message field.
46      */
47     std::optional<std::vector<std::string>> messageArgSources;
48 };
49 
50 /**
51  * @brief Represents the SRC related fields in the message registry.
52  *        It is part of the 'Entry' structure that will be filled in when
53  *        an error is looked up in the registry.
54  *
55  * If a field is wrapped by std::optional, it means the field is
56  * optional in the JSON and higher level code knows how to handle it.
57  */
58 struct SRC
59 {
60     /**
61      * @brief SRC type - The first byte of the ASCII string
62      */
63     uint8_t type;
64 
65     /**
66      * @brief The SRC reason code (2nd half of 4B 'ASCII string' word)
67      */
68     uint16_t reasonCode;
69 
70     /**
71      * @brief Specifies if the SRC represents a power fault.  Optional.
72      */
73     std::optional<bool> powerFault;
74 
75     /**
76      * @brief An optional vector of SRC hexword numbers that should be used
77      *        along with the SRC ASCII string to build the Symptom ID, which
78      *        is a field in the Extended Header section.
79      */
80     using WordNum = size_t;
81     std::optional<std::vector<WordNum>> symptomID;
82 
83     /**
84      * @brief Which AdditionalData fields to use to fill in the user defined
85      *        SRC hexwords.
86      *
87      * For example, if the AdditionalData event log property contained
88      * "CHIPNUM=42" and this map contained {6, CHIPNUM}, then the code
89      * would put 42 into SRC hexword 6.
90      */
91     using AdditionalDataField = std::string;
92     std::optional<std::map<WordNum, AdditionalDataField>> hexwordADFields;
93 
94     SRC() : type(0), reasonCode(0)
95     {
96     }
97 };
98 
99 /**
100  * @brief Represents a message registry entry, which is used for creating a
101  *        PEL from an OpenBMC event log.
102  */
103 struct Entry
104 {
105     /**
106      * @brief The error name, like "xyz.openbmc_project.Error.Foo".
107      */
108     std::string name;
109 
110     /**
111      * @brief The component ID of the PEL creator.
112      */
113     uint16_t componentID;
114 
115     /**
116      * @brief The PEL subsystem field.
117      */
118     uint8_t subsystem;
119 
120     /**
121      * @brief The optional PEL severity field.  If not specified, the PEL
122      *        will use the severity of the OpenBMC event log.
123      */
124     std::optional<uint8_t> severity;
125 
126     /**
127      * @brief The optional severity field to use when in manufacturing tolerance
128      *        mode.
129      */
130     std::optional<uint8_t> mfgSeverity;
131 
132     /**
133      * @brief The PEL action flags field.
134      */
135     std::optional<uint16_t> actionFlags;
136 
137     /**
138      * @brief  The optional action flags to use instead when in manufacturing
139      * tolerance mode.
140      */
141     std::optional<uint16_t> mfgActionFlags;
142 
143     /**
144      * @brief The PEL event type field.  If not specified, higher level code
145      *        will decide the value.
146      */
147     std::optional<uint8_t> eventType;
148 
149     /**
150      * @brief The PEL event scope field.  If not specified, higher level code
151      *        will decide the value.
152      */
153     std::optional<uint8_t> eventScope;
154 
155     /**
156      * The SRC related fields.
157      */
158     SRC src;
159 
160     /**
161      * The Documentation related fields.
162      */
163     DOC doc;
164 };
165 
166 /**
167  * @class Registry
168  *
169  * This class wraps the message registry JSON data and allows one to find
170  * the message registry entry pertaining to the error name.
171  *
172  * So that new registry files can easily be tested, the code will look for
173  * /etc/phosphor-logging/message_registry.json before looking for the real
174  * path.
175  */
176 class Registry
177 {
178   public:
179     Registry() = delete;
180     ~Registry() = default;
181     Registry(const Registry&) = default;
182     Registry& operator=(const Registry&) = default;
183     Registry(Registry&&) = default;
184     Registry& operator=(Registry&&) = default;
185 
186     /**
187      * @brief Constructor
188      * @param[in] registryFile - The path to the file.
189      */
190     explicit Registry(const std::filesystem::path& registryFile) :
191         _registryFile(registryFile)
192     {
193     }
194 
195     /**
196      * @brief Find a registry entry based on its error name or reason code.
197      *
198      * This function does do some basic sanity checking on the JSON contents,
199      * but there is also an external program that enforces a schema on the
200      * registry JSON that should catch all of these problems ahead of time.
201      *
202      * @param[in] name - The error name, like xyz.openbmc_project.Error.Foo
203      *                 - OR
204      *                 - The reason code, like 0x1001
205      * @param[in] type - LookupType enum value
206      * @param[in] toCache - boolean to cache registry in memory
207      * @return optional<Entry> A filled in message registry structure if
208      *                         found, otherwise an empty optional object.
209      */
210     std::optional<Entry> lookup(const std::string& name, LookupType type,
211                                 bool toCache = false);
212 
213   private:
214     /**
215      * @brief Parse message registry file using nlohmann::json
216      * @param[in] registryFile - The message registry JSON file
217      * @return optional<nlohmann::json> The full message registry object or an
218      *                                  empty optional object upon failure.
219      */
220     std::optional<nlohmann::json>
221         readRegistry(const std::filesystem::path& registryFile);
222 
223     /**
224      * @brief The path to the registry JSON file.
225      */
226     std::filesystem::path _registryFile;
227 
228     /**
229      * @brief The full message registry object.
230      */
231     std::optional<nlohmann::json> _registry;
232 };
233 
234 namespace helper
235 {
236 
237 /**
238  * @brief A helper function to get the PEL subsystem value based on
239  *        the registry subsystem name.
240  *
241  * @param[in] subsystemName - The registry name for the subsystem
242  *
243  * @return uint8_t The PEL subsystem value
244  */
245 uint8_t getSubsystem(const std::string& subsystemName);
246 
247 /**
248  * @brief A helper function to get the PEL severity value based on
249  *        the registry severity name.
250  *
251  * @param[in] severityName - The registry name for the severity
252  *
253  * @return uint8_t The PEL severity value
254  */
255 uint8_t getSeverity(const std::string& severityName);
256 
257 /**
258  * @brief A helper function to get the action flags value based on
259  *        the action flag names used in the registry.
260  *
261  * @param[in] flags - The list of flag names from the registry.
262  *
263  * @return uint16_t - The bitfield of flags used in the PEL.
264  */
265 uint16_t getActionFlags(const std::vector<std::string>& flags);
266 
267 /**
268  * @brief A helper function to get the PEL event type value based on
269  *        the registry event type name.
270  *
271  * @param[in] eventTypeName - The registry name for the event type
272  *
273  * @return uint8_t The PEL event type value
274  */
275 uint8_t getEventType(const std::string& eventTypeName);
276 
277 /**
278  * @brief A helper function to get the PEL event scope value based on
279  *        the registry event scope name.
280  *
281  * @param[in] eventScopeName - The registry name for the event scope
282  *
283  * @return uint8_t The PEL event scope value
284  */
285 uint8_t getEventScope(const std::string& eventScopeName);
286 
287 /**
288  * @brief Reads the "ReasonCode" field out of JSON and converts the string value
289  *        such as "0x5555" to a uint16 like 0x5555.
290  *
291  * @param[in] src - The message registry SRC dictionary to read from
292  * @param[in] name - The error name, to use in a trace if things go awry.
293  *
294  * @return uint16_t - The reason code
295  */
296 uint16_t getSRCReasonCode(const nlohmann::json& src, const std::string& name);
297 
298 /**
299  * @brief Reads the "Type" field out of JSON and converts it to the SRC::Type
300  *        value.
301  *
302  * @param[in] src - The message registry SRC dictionary to read from
303  * @param[in] name - The error name, to use in a trace if things go awry.
304  *
305  * @return uint8_t - The SRC type value, like 0x11
306  */
307 uint8_t getSRCType(const nlohmann::json& src, const std::string& name);
308 
309 /**
310  * @brief Reads the "Words6To9" field out of JSON and converts it to a map
311  *        of the SRC word number to the AdditionalData property field used
312  *        to fill it in with.
313  *
314  * @param[in] src - The message registry SRC dictionary to read from
315  * @param[in] name - The error name, to use in a trace if things go awry.
316  *
317  * @return std::optional<std::map<SRC::WordNum, SRC::AdditionalDataField>>
318  */
319 std::optional<std::map<SRC::WordNum, SRC::AdditionalDataField>>
320     getSRCHexwordFields(const nlohmann::json& src, const std::string& name);
321 
322 /**
323  * @brief Reads the "SymptomIDFields" field out of JSON and converts it to
324  *        a vector of SRC word numbers.
325  *
326  * @param[in] src - The message registry SRC dictionary to read from
327  * @param[in] name - The error name, to use in a trace if things go awry.
328  *
329  * @return std::optional<std::vector<SRC::WordNum>>
330  */
331 std::optional<std::vector<SRC::WordNum>>
332     getSRCSymptomIDFields(const nlohmann::json& src, const std::string& name);
333 
334 /**
335  * @brief Reads the "ComponentID" field out of JSON and converts it to a
336  *        uint16_t like 0xFF00.
337  *
338  * The ComponentID JSON field is only required if the SRC type isn't a BD
339  * BMC SRC, because for those SRCs it can be inferred from the upper byte
340  * of the SRC reasoncode.
341  *
342  * @param[in] srcType - The SRC type
343  * @param[in] reasonCode - The SRC reason code
344  * @param[in] pelEntry - The PEL entry JSON
345  * @param[in] name - The error name, to use in a trace if things go awry.
346  *
347  * @return uin16_t - The component ID, like 0xFF00
348  */
349 uint16_t getComponentID(uint8_t srcType, uint16_t reasonCode,
350                         const nlohmann::json& pelEntry,
351                         const std::string& name);
352 
353 } // namespace helper
354 
355 } // namespace message
356 
357 } // namespace pels
358 } // namespace openpower
359