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 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 void RasDataParser::initDataFiles() 52 { 53 iv_dataFiles.clear(); // initially empty 54 55 // Get the RAS data schema files from the package `schema` subdirectory. 56 fs::path schemaDir{PACKAGE_DIR "schema"}; 57 auto schemaRegex = R"(ras-data-schema-v[0-9]{2}\.json)"; 58 std::vector<fs::path> schemaPaths; 59 util::findFiles(schemaDir, schemaRegex, schemaPaths); 60 61 // Parse each of the schema files. 62 std::map<unsigned int, nlohmann::json> schemaFiles; 63 for (const auto& path : schemaPaths) 64 { 65 // Trace each data file for debug. 66 trace::inf("File found: path=%s", path.string().c_str()); 67 68 // Open the file. 69 std::ifstream file{path}; 70 assert(file.good()); // The file must be readable. 71 72 try 73 { 74 // Parse the JSON. 75 auto schema = nlohmann::json::parse(file); 76 77 // Get the schema version. 78 auto version = schema.at("version").get<unsigned int>(); 79 80 // Keep track of the schemas. 81 auto ret = schemaFiles.emplace(version, schema); 82 assert(ret.second); // Should not have duplicate entries 83 } 84 catch (...) 85 { 86 trace::err("Failed to parse file: %s", path.string().c_str()); 87 throw; // caught later downstream 88 } 89 } 90 91 // Get the RAS data files from the package `data` subdirectory. 92 fs::path dataDir{PACKAGE_DIR "ras-data"}; 93 std::vector<fs::path> dataPaths; 94 util::findFiles(dataDir, R"(.*\.json)", dataPaths); 95 96 // Parse each of the data files. 97 for (const auto& path : dataPaths) 98 { 99 // Trace each data file for debug. 100 trace::inf("File found: path=%s", path.string().c_str()); 101 102 // Open the file. 103 std::ifstream file{path}; 104 assert(file.good()); // The file must be readable. 105 106 try 107 { 108 // Parse the JSON. 109 const auto data = nlohmann::json::parse(file); 110 111 // Get the data version. 112 auto version = data.at("version").get<unsigned int>(); 113 114 // Get the schema for this file. 115 auto schema = schemaFiles.at(version); 116 117 // Validate the data against the schema. 118 assert(util::validateJson(schema, data)); 119 120 // Get the chip model/EC level from the data. The value is currently 121 // stored as a string representation of the hex value. So it will 122 // have to be converted to an integer. 123 libhei::ChipType_t chipType = 124 std::stoul(data.at("model_ec").get<std::string>(), 0, 16); 125 126 // So far, so good. Add the entry. 127 auto ret = iv_dataFiles.emplace(chipType, data); 128 assert(ret.second); // Should not have duplicate entries 129 } 130 catch (...) 131 { 132 trace::err("Failed to parse file: %s", path.string().c_str()); 133 throw; // caught later downstream 134 } 135 } 136 } 137 138 //------------------------------------------------------------------------------ 139 140 std::string RasDataParser::parseSignature(const nlohmann::json& i_data, 141 const libhei::Signature& i_signature) 142 { 143 // Get the signature keys. All are hex (lower case) with no prefix. 144 char buf[5]; 145 sprintf(buf, "%04x", i_signature.getId()); 146 std::string id{buf}; 147 148 sprintf(buf, "%02x", i_signature.getBit()); 149 std::string bit{buf}; 150 151 sprintf(buf, "%02x", i_signature.getInstance()); 152 std::string inst{buf}; 153 154 std::string action; 155 156 try 157 { 158 action = 159 i_data.at("signatures").at(id).at(bit).at(inst).get<std::string>(); 160 } 161 catch (const std::out_of_range& e) 162 { 163 trace::err("No action defined for signature: %s %s %s", id.c_str(), 164 bit.c_str(), inst.c_str()); 165 throw; // caught later downstream 166 } 167 168 // Return the action. 169 return action; 170 } 171 172 //------------------------------------------------------------------------------ 173 174 std::tuple<callout::BusType, std::string> 175 RasDataParser::parseBus(const nlohmann::json& i_data, 176 const std::string& i_name) 177 { 178 auto bus = i_data.at("buses").at(i_name); 179 180 // clang-format off 181 static const std::map<std::string, callout::BusType> m = 182 { 183 {"SMP_BUS", callout::BusType::SMP_BUS}, 184 {"OMI_BUS", callout::BusType::OMI_BUS}, 185 }; 186 // clang-format on 187 188 auto busType = m.at(bus.at("type").get<std::string>()); 189 190 std::string unitPath{}; // default empty if unit does not exist 191 if (bus.contains("unit")) 192 { 193 auto unit = bus.at("unit").get<std::string>(); 194 unitPath = i_data.at("units").at(unit).get<std::string>(); 195 } 196 197 return std::make_tuple(busType, unitPath); 198 } 199 200 //------------------------------------------------------------------------------ 201 202 std::shared_ptr<Resolution> 203 RasDataParser::parseAction(const nlohmann::json& i_data, 204 const std::string& i_action) 205 { 206 auto o_list = std::make_shared<ResolutionList>(); 207 208 // This function will be called recursively and we want to prevent cyclic 209 // recursion. 210 static std::vector<std::string> stack; 211 assert(stack.end() == std::find(stack.begin(), stack.end(), i_action)); 212 stack.push_back(i_action); 213 214 // Iterate the action list and apply the changes. 215 for (const auto& a : i_data.at("actions").at(i_action)) 216 { 217 auto type = a.at("type").get<std::string>(); 218 219 if ("action" == type) 220 { 221 auto name = a.at("name").get<std::string>(); 222 223 o_list->push(parseAction(i_data, name)); 224 } 225 else if ("callout_self" == type) 226 { 227 auto priority = a.at("priority").get<std::string>(); 228 auto guard = a.at("guard").get<bool>(); 229 230 std::string path{}; // Must be empty to callout the chip. 231 232 o_list->push(std::make_shared<HardwareCalloutResolution>( 233 path, getPriority(priority), guard)); 234 } 235 else if ("callout_unit" == type) 236 { 237 auto name = a.at("name").get<std::string>(); 238 auto priority = a.at("priority").get<std::string>(); 239 auto guard = a.at("guard").get<bool>(); 240 241 auto path = i_data.at("units").at(name).get<std::string>(); 242 243 o_list->push(std::make_shared<HardwareCalloutResolution>( 244 path, getPriority(priority), guard)); 245 } 246 else if ("callout_connected" == type) 247 { 248 auto name = a.at("name").get<std::string>(); 249 auto priority = a.at("priority").get<std::string>(); 250 auto guard = a.at("guard").get<bool>(); 251 252 auto busData = parseBus(i_data, name); 253 254 o_list->push(std::make_shared<ConnectedCalloutResolution>( 255 std::get<0>(busData), std::get<1>(busData), 256 getPriority(priority), guard)); 257 } 258 else if ("callout_bus" == type) 259 { 260 auto name = a.at("name").get<std::string>(); 261 auto priority = a.at("priority").get<std::string>(); 262 auto guard = a.at("guard").get<bool>(); 263 264 auto busData = parseBus(i_data, name); 265 266 o_list->push(std::make_shared<BusCalloutResolution>( 267 std::get<0>(busData), std::get<1>(busData), 268 getPriority(priority), guard)); 269 } 270 else if ("callout_clock" == type) 271 { 272 auto name = a.at("name").get<std::string>(); 273 auto priority = a.at("priority").get<std::string>(); 274 auto guard = a.at("guard").get<bool>(); 275 276 // clang-format off 277 static const std::map<std::string, callout::ClockType> m = 278 { 279 {"OSC_REF_CLOCK_0", callout::ClockType::OSC_REF_CLOCK_0}, 280 {"OSC_REF_CLOCK_1", callout::ClockType::OSC_REF_CLOCK_1}, 281 {"TOD_CLOCK", callout::ClockType::TOD_CLOCK}, 282 }; 283 // clang-format on 284 285 o_list->push(std::make_shared<ClockCalloutResolution>( 286 m.at(name), getPriority(priority), guard)); 287 } 288 else if ("callout_procedure" == type) 289 { 290 auto name = a.at("name").get<std::string>(); 291 auto priority = a.at("priority").get<std::string>(); 292 293 // clang-format off 294 static const std::map<std::string, callout::Procedure> m = 295 { 296 {"LEVEL2", callout::Procedure::NEXTLVL}, 297 {"SUE_SEEN", callout::Procedure::SUE_SEEN}, 298 }; 299 // clang-format on 300 301 o_list->push(std::make_shared<ProcedureCalloutResolution>( 302 m.at(name), getPriority(priority))); 303 } 304 else if ("callout_part" == type) 305 { 306 auto name = a.at("name").get<std::string>(); 307 auto priority = a.at("priority").get<std::string>(); 308 309 // clang-format off 310 static const std::map<std::string, callout::PartType> m = 311 { 312 {"PNOR", callout::PartType::PNOR}, 313 }; 314 // clang-format on 315 316 o_list->push(std::make_shared<PartCalloutResolution>( 317 m.at(name), getPriority(priority))); 318 } 319 else if ("plugin" == type) 320 { 321 auto name = a.at("name").get<std::string>(); 322 auto inst = a.at("instance").get<unsigned int>(); 323 324 o_list->push(std::make_shared<PluginResolution>(name, inst)); 325 } 326 else 327 { 328 throw std::logic_error("Unsupported action type: " + type); 329 } 330 } 331 332 // Done with this action pop it off the stack. 333 stack.pop_back(); 334 335 return o_list; 336 } 337 338 //------------------------------------------------------------------------------ 339 340 callout::Priority RasDataParser::getPriority(const std::string& i_priority) 341 { 342 // clang-format off 343 static const std::map<std::string, callout::Priority> m = 344 { 345 {"HIGH", callout::Priority::HIGH}, 346 {"MED", callout::Priority::MED}, 347 {"MED_A", callout::Priority::MED_A}, 348 {"MED_B", callout::Priority::MED_B}, 349 {"MED_C", callout::Priority::MED_C}, 350 {"LOW", callout::Priority::LOW}, 351 }; 352 // clang-format on 353 354 return m.at(i_priority); 355 } 356 357 //------------------------------------------------------------------------------ 358 359 } // namespace analyzer 360