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