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