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