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