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