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