1 #include <analyzer/ras-data/ras-data-parser.hpp> 2 #include <util/data_file.hpp> 3 #include <util/trace.hpp> 4 5 #include <filesystem> 6 #include <fstream> 7 #include <stdexcept> 8 #include <string> 9 10 namespace fs = std::filesystem; 11 12 namespace analyzer 13 { 14 //------------------------------------------------------------------------------ 15 16 std::shared_ptr<Resolution> 17 RasDataParser::getResolution(const libhei::Signature& i_signature) 18 { 19 nlohmann::json data; 20 21 try 22 { 23 data = iv_dataFiles.at(i_signature.getChip().getType()); 24 } 25 catch (const std::out_of_range& e) 26 { 27 trace::err("No RAS data defined for chip type: 0x%08x", 28 i_signature.getChip().getType()); 29 throw; // caught later downstream 30 } 31 32 const auto action = parseSignature(data, i_signature); 33 34 std::shared_ptr<Resolution> resolution; 35 36 try 37 { 38 resolution = parseAction(data, action); 39 } 40 catch (...) 41 { 42 trace::err("Unable to get resolution for action: %s", action.c_str()); 43 throw; // caught later downstream 44 } 45 46 return resolution; 47 } 48 49 //------------------------------------------------------------------------------ 50 51 bool __checkActionForFlag(const std::string& i_action, 52 const std::string& i_flag, 53 const nlohmann::json& i_data) 54 { 55 bool o_isFlagSet = false; 56 57 // Loop through the array of actions. 58 for (const auto& a : i_data.at("actions").at(i_action)) 59 { 60 // Get the action type 61 auto type = a.at("type").get<std::string>(); 62 63 // If the action is another action, recursively call this function 64 if ("action" == type) 65 { 66 auto name = a.at("name").get<std::string>(); 67 o_isFlagSet = __checkActionForFlag(name, i_flag, i_data); 68 if (o_isFlagSet) 69 { 70 break; 71 } 72 } 73 // If the action is a flag, check if it's the one 74 else if ("flag" == type) 75 { 76 auto name = a.at("name").get<std::string>(); 77 if (name == i_flag) 78 { 79 o_isFlagSet = true; 80 break; 81 } 82 } 83 } 84 85 return o_isFlagSet; 86 } 87 88 //------------------------------------------------------------------------------ 89 90 bool RasDataParser::isFlagSet(const libhei::Signature& i_signature, 91 const RasDataFlags i_flag) const 92 { 93 bool o_isFlagSet = false; 94 95 // List of all flag enums mapping to their corresponding string 96 std::map<RasDataFlags, std::string> flagMap = { 97 {SUE_SOURCE, "sue_source"}, 98 {SUE_SEEN, "sue_seen"}, 99 {CS_POSSIBLE, "cs_possible"}, 100 {RECOVERED_ERROR, "recovered_error"}, 101 {INFORMATIONAL_ONLY, "informational_only"}, 102 {MNFG_INFORMATIONAL_ONLY, "mnfg_informational_only"}, 103 {MASK_BUT_DONT_CLEAR, "mask_but_dont_clear"}, 104 {CRC_RELATED_ERR, "crc_related_err"}, 105 {CRC_ROOT_CAUSE, "crc_root_cause"}, 106 {ODP_DATA_CORRUPT_SIDE_EFFECT, "odp_data_corrupt_side_effect"}, 107 {ODP_DATA_CORRUPT_ROOT_CAUSE, "odp_data_corrupt_root_cause"}, 108 {ATTN_FROM_OCMB, "attn_from_ocmb"}, 109 }; 110 std::string strFlag = flagMap[i_flag]; 111 112 // If the input flag does not exist in the map, that's a code bug. 113 assert(0 != flagMap.count(i_flag)); 114 115 nlohmann::json data; 116 try 117 { 118 data = iv_dataFiles.at(i_signature.getChip().getType()); 119 } 120 catch (const std::out_of_range& e) 121 { 122 trace::err("No RAS data defined for chip type: 0x%08x", 123 i_signature.getChip().getType()); 124 throw; // caught later downstream 125 } 126 127 // Get the signature keys. All are hex (lower case) with no prefix. 128 char buf[5]; 129 sprintf(buf, "%04x", i_signature.getId()); 130 std::string id{buf}; 131 132 sprintf(buf, "%02x", i_signature.getBit()); 133 std::string bit{buf}; 134 135 // Get the list of flags in string format from the data. 136 try 137 { 138 auto flags = data.at("signatures") 139 .at(id) 140 .at(bit) 141 .at("flags") 142 .get<std::vector<std::string>>(); 143 144 // Check if the input flag exists 145 if (flags.end() != std::find(flags.begin(), flags.end(), strFlag)) 146 { 147 o_isFlagSet = true; 148 } 149 } 150 catch (const nlohmann::json::out_of_range& e) 151 { 152 // Do nothing. Assume there is no flag defined. If for some reason 153 // the `id` or `bit` were not defined, that will be cause below when the 154 // signture is parsed. 155 } 156 157 // If the flag hasn't been found, check if it was defined as part of the 158 // action for this input signature. 159 if (!o_isFlagSet) 160 { 161 const auto action = parseSignature(data, i_signature); 162 try 163 { 164 __checkActionForFlag(action, strFlag, data); 165 } 166 catch (const nlohmann::json::out_of_range& e) 167 { 168 // Again, do nothing. Assume there is no flag defined. If for some 169 // reason the action is not defined, that will be handled later when 170 // attempting to get the resolution. 171 } 172 } 173 174 return o_isFlagSet; 175 } 176 177 //------------------------------------------------------------------------------ 178 179 unsigned int 180 RasDataParser::getVersion(const libhei::Signature& i_signature) const 181 { 182 unsigned int o_version = 0; 183 184 nlohmann::json data; 185 try 186 { 187 data = iv_dataFiles.at(i_signature.getChip().getType()); 188 } 189 catch (const std::out_of_range& e) 190 { 191 trace::err("No RAS data defined for chip type: 0x%08x", 192 i_signature.getChip().getType()); 193 throw; // caught later downstream 194 } 195 196 o_version = data.at("version").get<unsigned int>(); 197 198 return o_version; 199 } 200 201 //------------------------------------------------------------------------------ 202 203 void RasDataParser::initDataFiles() 204 { 205 iv_dataFiles.clear(); // initially empty 206 207 // Get the RAS data schema files from the package `schema` subdirectory. 208 fs::path schemaDir{PACKAGE_DIR "schema"}; 209 auto schemaRegex = R"(ras-data-schema-v[0-9]{2}\.json)"; 210 std::vector<fs::path> schemaPaths; 211 util::findFiles(schemaDir, schemaRegex, schemaPaths); 212 213 // Parse each of the schema files. 214 std::map<unsigned int, nlohmann::json> schemaFiles; 215 for (const auto& path : schemaPaths) 216 { 217 // Trace each data file for debug. 218 trace::inf("File found: path=%s", path.string().c_str()); 219 220 // Open the file. 221 std::ifstream file{path}; 222 assert(file.good()); // The file must be readable. 223 224 try 225 { 226 // Parse the JSON. 227 auto schema = nlohmann::json::parse(file); 228 229 // Get the schema version. 230 auto version = schema.at("version").get<unsigned int>(); 231 assert(2 <= version); // check support version 232 233 // Keep track of the schemas. 234 auto ret = schemaFiles.emplace(version, schema); 235 assert(ret.second); // Should not have duplicate entries 236 } 237 catch (...) 238 { 239 trace::err("Failed to parse file: %s", path.string().c_str()); 240 throw; // caught later downstream 241 } 242 } 243 244 // Get the RAS data files from the package `data` subdirectory. 245 fs::path dataDir{PACKAGE_DIR "ras-data"}; 246 std::vector<fs::path> dataPaths; 247 util::findFiles(dataDir, R"(.*\.json)", dataPaths); 248 249 // Parse each of the data files. 250 for (const auto& path : dataPaths) 251 { 252 // Trace each data file for debug. 253 trace::inf("File found: path=%s", path.string().c_str()); 254 255 // Open the file. 256 std::ifstream file{path}; 257 assert(file.good()); // The file must be readable. 258 259 try 260 { 261 // Parse the JSON. 262 const auto data = nlohmann::json::parse(file); 263 264 // Get the data version. 265 auto version = data.at("version").get<unsigned int>(); 266 assert(2 <= version); // check support version 267 268 // Get the schema for this file. 269 auto schema = schemaFiles.at(version); 270 271 // Validate the data against the schema. 272 assert(util::validateJson(schema, data)); 273 274 // Get the chip model/EC level from the data. The value is currently 275 // stored as a string representation of the hex value. So it will 276 // have to be converted to an integer. 277 libhei::ChipType_t chipType = 278 std::stoul(data.at("model_ec").get<std::string>(), 0, 16); 279 280 // So far, so good. Add the entry. 281 auto ret = iv_dataFiles.emplace(chipType, data); 282 assert(ret.second); // Should not have duplicate entries 283 } 284 catch (...) 285 { 286 trace::err("Failed to parse file: %s", path.string().c_str()); 287 throw; // caught later downstream 288 } 289 } 290 } 291 292 //------------------------------------------------------------------------------ 293 294 std::string 295 RasDataParser::parseSignature(const nlohmann::json& i_data, 296 const libhei::Signature& i_signature) const 297 { 298 // Get the signature keys. All are hex (lower case) with no prefix. 299 char buf[5]; 300 sprintf(buf, "%04x", i_signature.getId()); 301 std::string id{buf}; 302 303 sprintf(buf, "%02x", i_signature.getBit()); 304 std::string bit{buf}; 305 306 sprintf(buf, "%02x", i_signature.getInstance()); 307 std::string inst{buf}; 308 309 std::string action; 310 311 try 312 { 313 action = 314 i_data.at("signatures").at(id).at(bit).at(inst).get<std::string>(); 315 } 316 catch (const nlohmann::json::out_of_range& e) 317 { 318 trace::err("No action defined for signature: %s %s %s", id.c_str(), 319 bit.c_str(), inst.c_str()); 320 321 // Default to 'level2_M_th1' if no signature is found. 322 action = "level2_M_th1"; 323 } 324 325 // Return the action. 326 return action; 327 } 328 329 //------------------------------------------------------------------------------ 330 331 std::tuple<callout::BusType, std::string> 332 RasDataParser::parseBus(const nlohmann::json& i_data, 333 const std::string& i_name) 334 { 335 auto bus = i_data.at("buses").at(i_name); 336 337 // clang-format off 338 static const std::map<std::string, callout::BusType> m = 339 { 340 {"SMP_BUS", callout::BusType::SMP_BUS}, 341 {"OMI_BUS", callout::BusType::OMI_BUS}, 342 }; 343 // clang-format on 344 345 auto busType = m.at(bus.at("type").get<std::string>()); 346 347 std::string unitPath{}; // default empty if unit does not exist 348 if (bus.contains("unit")) 349 { 350 auto unit = bus.at("unit").get<std::string>(); 351 unitPath = i_data.at("units").at(unit).get<std::string>(); 352 } 353 354 return std::make_tuple(busType, unitPath); 355 } 356 357 //------------------------------------------------------------------------------ 358 359 std::shared_ptr<Resolution> 360 RasDataParser::parseAction(const nlohmann::json& i_data, 361 const std::string& i_action) 362 { 363 auto o_list = std::make_shared<ResolutionList>(); 364 365 // This function will be called recursively and we want to prevent cyclic 366 // recursion. 367 static std::vector<std::string> stack; 368 assert(stack.end() == std::find(stack.begin(), stack.end(), i_action)); 369 stack.push_back(i_action); 370 371 // Iterate the action list and apply the changes. 372 for (const auto& a : i_data.at("actions").at(i_action)) 373 { 374 auto type = a.at("type").get<std::string>(); 375 376 if ("action" == type) 377 { 378 auto name = a.at("name").get<std::string>(); 379 380 o_list->push(parseAction(i_data, name)); 381 } 382 else if ("callout_self" == type) 383 { 384 auto priority = a.at("priority").get<std::string>(); 385 auto guard = a.at("guard").get<bool>(); 386 387 std::string path{}; // Must be empty to callout the chip. 388 389 o_list->push(std::make_shared<HardwareCalloutResolution>( 390 path, getPriority(priority), guard)); 391 } 392 else if ("callout_unit" == type) 393 { 394 auto name = a.at("name").get<std::string>(); 395 auto priority = a.at("priority").get<std::string>(); 396 auto guard = a.at("guard").get<bool>(); 397 398 auto path = i_data.at("units").at(name).get<std::string>(); 399 400 o_list->push(std::make_shared<HardwareCalloutResolution>( 401 path, getPriority(priority), guard)); 402 } 403 else if ("callout_connected" == type) 404 { 405 auto name = a.at("name").get<std::string>(); 406 auto priority = a.at("priority").get<std::string>(); 407 auto guard = a.at("guard").get<bool>(); 408 409 auto busData = parseBus(i_data, name); 410 411 o_list->push(std::make_shared<ConnectedCalloutResolution>( 412 std::get<0>(busData), std::get<1>(busData), 413 getPriority(priority), guard)); 414 } 415 else if ("callout_bus" == type) 416 { 417 auto name = a.at("name").get<std::string>(); 418 auto priority = a.at("priority").get<std::string>(); 419 auto guard = a.at("guard").get<bool>(); 420 421 auto busData = parseBus(i_data, name); 422 423 o_list->push(std::make_shared<BusCalloutResolution>( 424 std::get<0>(busData), std::get<1>(busData), 425 getPriority(priority), guard)); 426 } 427 else if ("callout_clock" == type) 428 { 429 auto name = a.at("name").get<std::string>(); 430 auto priority = a.at("priority").get<std::string>(); 431 auto guard = a.at("guard").get<bool>(); 432 433 // clang-format off 434 static const std::map<std::string, callout::ClockType> m = 435 { 436 {"OSC_REF_CLOCK_0", callout::ClockType::OSC_REF_CLOCK_0}, 437 {"OSC_REF_CLOCK_1", callout::ClockType::OSC_REF_CLOCK_1}, 438 {"TOD_CLOCK", callout::ClockType::TOD_CLOCK}, 439 }; 440 // clang-format on 441 442 o_list->push(std::make_shared<ClockCalloutResolution>( 443 m.at(name), getPriority(priority), guard)); 444 } 445 else if ("callout_procedure" == type) 446 { 447 auto name = a.at("name").get<std::string>(); 448 auto priority = a.at("priority").get<std::string>(); 449 450 // clang-format off 451 static const std::map<std::string, callout::Procedure> m = 452 { 453 {"LEVEL2", callout::Procedure::NEXTLVL}, 454 {"SUE_SEEN", callout::Procedure::SUE_SEEN}, 455 }; 456 // clang-format on 457 458 o_list->push(std::make_shared<ProcedureCalloutResolution>( 459 m.at(name), getPriority(priority))); 460 } 461 else if ("callout_part" == type) 462 { 463 auto name = a.at("name").get<std::string>(); 464 auto priority = a.at("priority").get<std::string>(); 465 466 // clang-format off 467 static const std::map<std::string, callout::PartType> m = 468 { 469 {"PNOR", callout::PartType::PNOR}, 470 }; 471 // clang-format on 472 473 o_list->push(std::make_shared<PartCalloutResolution>( 474 m.at(name), getPriority(priority))); 475 } 476 else if ("plugin" == type) 477 { 478 auto name = a.at("name").get<std::string>(); 479 auto inst = a.at("instance").get<unsigned int>(); 480 481 o_list->push(std::make_shared<PluginResolution>(name, inst)); 482 } 483 else if ("flag" == type) 484 { 485 // No action, flags will be handled with the isFlagSet function 486 } 487 else 488 { 489 throw std::logic_error("Unsupported action type: " + type); 490 } 491 } 492 493 // Done with this action pop it off the stack. 494 stack.pop_back(); 495 496 return o_list; 497 } 498 499 //------------------------------------------------------------------------------ 500 501 callout::Priority RasDataParser::getPriority(const std::string& i_priority) 502 { 503 // clang-format off 504 static const std::map<std::string, callout::Priority> m = 505 { 506 {"HIGH", callout::Priority::HIGH}, 507 {"MED", callout::Priority::MED}, 508 {"MED_A", callout::Priority::MED_A}, 509 {"MED_B", callout::Priority::MED_B}, 510 {"MED_C", callout::Priority::MED_C}, 511 {"LOW", callout::Priority::LOW}, 512 }; 513 // clang-format on 514 515 return m.at(i_priority); 516 } 517 518 //------------------------------------------------------------------------------ 519 520 } // namespace analyzer 521