1a9b44344SZane Shelley #include <analyzer/ras-data/ras-data-parser.hpp>
2a9b44344SZane Shelley #include <util/data_file.hpp>
3a9b44344SZane Shelley #include <util/trace.hpp>
4a9b44344SZane Shelley 
5a9b44344SZane Shelley #include <filesystem>
6a9b44344SZane Shelley #include <fstream>
7a9b44344SZane Shelley #include <string>
8a9b44344SZane Shelley 
9a9b44344SZane Shelley namespace fs = std::filesystem;
10a9b44344SZane Shelley 
11a9b44344SZane Shelley namespace analyzer
12a9b44344SZane Shelley {
13a9b44344SZane Shelley 
14a9b44344SZane Shelley //------------------------------------------------------------------------------
15a9b44344SZane Shelley 
16a9b44344SZane Shelley std::shared_ptr<Resolution>
177698b304SZane Shelley     RasDataParser::getResolution(const libhei::Signature& i_signature)
18a9b44344SZane Shelley {
197698b304SZane Shelley     const auto data = iv_dataFiles.at(i_signature.getChip().getType());
207698b304SZane Shelley 
217698b304SZane Shelley     const auto action = parseSignature(data, i_signature);
227698b304SZane Shelley 
237698b304SZane Shelley     return parseAction(data, action);
24a9b44344SZane Shelley }
25a9b44344SZane Shelley 
26a9b44344SZane Shelley //------------------------------------------------------------------------------
27a9b44344SZane Shelley 
28a9b44344SZane Shelley void RasDataParser::initDataFiles()
29a9b44344SZane Shelley {
30a9b44344SZane Shelley     iv_dataFiles.clear(); // initially empty
31a9b44344SZane Shelley 
32a9b44344SZane Shelley     // Get the RAS data schema files from the package `schema` subdirectory.
33a9b44344SZane Shelley     fs::path schemaDir{PACKAGE_DIR "schema"};
34ee54c99fSZane Shelley     auto schemaRegex = R"(ras-data-schema-v[0-9]{2}\.json)";
35ee54c99fSZane Shelley     std::vector<fs::path> schemaPaths;
36ee54c99fSZane Shelley     util::findFiles(schemaDir, schemaRegex, schemaPaths);
37a9b44344SZane Shelley 
38ee54c99fSZane Shelley     // Parse each of the schema files.
39ee54c99fSZane Shelley     std::map<unsigned int, nlohmann::json> schemaFiles;
40ee54c99fSZane Shelley     for (const auto& path : schemaPaths)
41ee54c99fSZane Shelley     {
42ee54c99fSZane Shelley         // Trace each data file for debug.
43ee54c99fSZane Shelley         trace::inf("File found: path=%s", path.string().c_str());
44a9b44344SZane Shelley 
45a9b44344SZane Shelley         // Open the file.
46ee54c99fSZane Shelley         std::ifstream file{path};
47ee54c99fSZane Shelley         assert(file.good()); // The file must be readable.
48a9b44344SZane Shelley 
49a9b44344SZane Shelley         // Parse the JSON.
50ee54c99fSZane Shelley         auto schema = nlohmann::json::parse(file);
51a9b44344SZane Shelley 
52ee54c99fSZane Shelley         // Get the schema version.
53ee54c99fSZane Shelley         auto version = schema.at("version").get<unsigned int>();
54ee54c99fSZane Shelley 
55ee54c99fSZane Shelley         // Keep track of the schemas.
56ee54c99fSZane Shelley         auto ret = schemaFiles.emplace(version, schema);
57ee54c99fSZane Shelley         assert(ret.second); // Should not have duplicate entries
58ee54c99fSZane Shelley     }
59ee54c99fSZane Shelley 
60ee54c99fSZane Shelley     // Get the RAS data files from the package `data` subdirectory.
61a9b44344SZane Shelley     fs::path dataDir{PACKAGE_DIR "ras-data"};
62a9b44344SZane Shelley     std::vector<fs::path> dataPaths;
63a9b44344SZane Shelley     util::findFiles(dataDir, R"(.*\.json)", dataPaths);
64a9b44344SZane Shelley 
65a9b44344SZane Shelley     // Parse each of the data files.
66a9b44344SZane Shelley     for (const auto& path : dataPaths)
67a9b44344SZane Shelley     {
68a9b44344SZane Shelley         // Trace each data file for debug.
69a9b44344SZane Shelley         trace::inf("File found: path=%s", path.string().c_str());
70a9b44344SZane Shelley 
71a9b44344SZane Shelley         // Open the file.
72a9b44344SZane Shelley         std::ifstream file{path};
73a9b44344SZane Shelley         assert(file.good()); // The file must be readable.
74a9b44344SZane Shelley 
75a9b44344SZane Shelley         // Parse the JSON.
76a9b44344SZane Shelley         const auto data = nlohmann::json::parse(file);
77a9b44344SZane Shelley 
78ee54c99fSZane Shelley         // Get the data version.
79ee54c99fSZane Shelley         auto version = data.at("version").get<unsigned int>();
80ee54c99fSZane Shelley 
81ee54c99fSZane Shelley         // Get the schema for this file.
82ee54c99fSZane Shelley         auto schema = schemaFiles.at(version);
83ee54c99fSZane Shelley 
84a9b44344SZane Shelley         // Validate the data against the schema.
85a9b44344SZane Shelley         assert(util::validateJson(schema, data));
86a9b44344SZane Shelley 
87a9b44344SZane Shelley         // Get the chip model/EC level from the data. The value is currently
88a9b44344SZane Shelley         // stored as a string representation of the hex value. So it will have
89a9b44344SZane Shelley         // to be converted to an integer.
90a9b44344SZane Shelley         libhei::ChipType_t chipType =
91a9b44344SZane Shelley             std::stoul(data.at("model_ec").get<std::string>(), 0, 16);
92a9b44344SZane Shelley 
93a9b44344SZane Shelley         // So far, so good. Add the entry.
94a9b44344SZane Shelley         auto ret = iv_dataFiles.emplace(chipType, data);
95a9b44344SZane Shelley         assert(ret.second); // Should not have duplicate entries
96a9b44344SZane Shelley     }
97a9b44344SZane Shelley }
98a9b44344SZane Shelley 
99a9b44344SZane Shelley //------------------------------------------------------------------------------
100a9b44344SZane Shelley 
1017698b304SZane Shelley std::string RasDataParser::parseSignature(const nlohmann::json& i_data,
1027698b304SZane Shelley                                           const libhei::Signature& i_signature)
1037698b304SZane Shelley {
1047698b304SZane Shelley     // Get the signature keys. All are hex (lower case) with no prefix.
1057698b304SZane Shelley     char buf[5];
1067698b304SZane Shelley     sprintf(buf, "%04x", i_signature.getId());
1077698b304SZane Shelley     std::string id{buf};
1087698b304SZane Shelley 
1097698b304SZane Shelley     sprintf(buf, "%02x", i_signature.getBit());
1107698b304SZane Shelley     std::string bit{buf};
1117698b304SZane Shelley 
1127698b304SZane Shelley     sprintf(buf, "%02x", i_signature.getInstance());
1137698b304SZane Shelley     std::string inst{buf};
1147698b304SZane Shelley 
1157698b304SZane Shelley     // Return the action.
1167698b304SZane Shelley     return i_data.at("signatures").at(id).at(bit).at(inst).get<std::string>();
1177698b304SZane Shelley }
1187698b304SZane Shelley 
1197698b304SZane Shelley //------------------------------------------------------------------------------
1207698b304SZane Shelley 
1215d63cefcSZane Shelley std::tuple<callout::BusType, std::string>
1225d63cefcSZane Shelley     RasDataParser::parseBus(const nlohmann::json& i_data,
1235d63cefcSZane Shelley                             const std::string& i_name)
1245d63cefcSZane Shelley {
1255d63cefcSZane Shelley     auto bus = i_data.at("buses").at(i_name);
1265d63cefcSZane Shelley 
1275d63cefcSZane Shelley     // clang-format off
1285d63cefcSZane Shelley     static const std::map<std::string, callout::BusType> m =
1295d63cefcSZane Shelley     {
1305d63cefcSZane Shelley         {"SMP_BUS", callout::BusType::SMP_BUS},
1315d63cefcSZane Shelley         {"OMI_BUS", callout::BusType::OMI_BUS},
1325d63cefcSZane Shelley     };
1335d63cefcSZane Shelley     // clang-format on
1345d63cefcSZane Shelley 
1355d63cefcSZane Shelley     auto busType = m.at(bus.at("type").get<std::string>());
1365d63cefcSZane Shelley 
1375d63cefcSZane Shelley     std::string unitPath{}; // default empty if unit does not exist
1385d63cefcSZane Shelley     if (bus.contains("unit"))
1395d63cefcSZane Shelley     {
1405d63cefcSZane Shelley         unitPath = bus.at("unit").get<std::string>();
1415d63cefcSZane Shelley     }
1425d63cefcSZane Shelley 
1435d63cefcSZane Shelley     return std::make_tuple(busType, unitPath);
1445d63cefcSZane Shelley }
1455d63cefcSZane Shelley 
1465d63cefcSZane Shelley //------------------------------------------------------------------------------
1475d63cefcSZane Shelley 
1487698b304SZane Shelley std::shared_ptr<Resolution>
1497698b304SZane Shelley     RasDataParser::parseAction(const nlohmann::json& i_data,
1507698b304SZane Shelley                                const std::string& i_action)
1517698b304SZane Shelley {
1527698b304SZane Shelley     auto o_list = std::make_shared<ResolutionList>();
1537698b304SZane Shelley 
1547698b304SZane Shelley     // This function will be called recursively and we want to prevent cyclic
1557698b304SZane Shelley     // recursion.
1567698b304SZane Shelley     static std::vector<std::string> stack;
1577698b304SZane Shelley     assert(stack.end() == std::find(stack.begin(), stack.end(), i_action));
1587698b304SZane Shelley     stack.push_back(i_action);
1597698b304SZane Shelley 
1607698b304SZane Shelley     // Iterate the action list and apply the changes.
1617698b304SZane Shelley     for (const auto& a : i_data.at("actions").at(i_action))
1627698b304SZane Shelley     {
1637698b304SZane Shelley         auto type = a.at("type").get<std::string>();
1647698b304SZane Shelley 
1657698b304SZane Shelley         if ("action" == type)
1667698b304SZane Shelley         {
1677698b304SZane Shelley             auto name = a.at("name").get<std::string>();
1687698b304SZane Shelley 
1697698b304SZane Shelley             o_list->push(parseAction(i_data, name));
1707698b304SZane Shelley         }
1717698b304SZane Shelley         else if ("callout_self" == type)
1727698b304SZane Shelley         {
1737698b304SZane Shelley             auto priority = a.at("priority").get<std::string>();
1747698b304SZane Shelley             auto guard    = a.at("guard").get<bool>();
1757698b304SZane Shelley 
176e4bfb47cSZane Shelley             std::string path{}; // Must be empty to callout the chip.
177e4bfb47cSZane Shelley 
178e4bfb47cSZane Shelley             o_list->push(std::make_shared<HardwareCalloutResolution>(
179e4bfb47cSZane Shelley                 path, getPriority(priority), guard));
1807698b304SZane Shelley         }
1817698b304SZane Shelley         else if ("callout_unit" == type)
1827698b304SZane Shelley         {
1837698b304SZane Shelley             auto name     = a.at("name").get<std::string>();
1847698b304SZane Shelley             auto priority = a.at("priority").get<std::string>();
1857698b304SZane Shelley             auto guard    = a.at("guard").get<bool>();
1867698b304SZane Shelley 
187e4bfb47cSZane Shelley             auto path = i_data.at("units").at(name).get<std::string>();
188e4bfb47cSZane Shelley 
189e4bfb47cSZane Shelley             o_list->push(std::make_shared<HardwareCalloutResolution>(
190e4bfb47cSZane Shelley                 path, getPriority(priority), guard));
1917698b304SZane Shelley         }
1927698b304SZane Shelley         else if ("callout_connected" == type)
1937698b304SZane Shelley         {
1947698b304SZane Shelley             auto name     = a.at("name").get<std::string>();
1957698b304SZane Shelley             auto priority = a.at("priority").get<std::string>();
1967698b304SZane Shelley             auto guard    = a.at("guard").get<bool>();
1977698b304SZane Shelley 
1985d63cefcSZane Shelley             auto busData = parseBus(i_data, name);
1995d63cefcSZane Shelley 
2005d63cefcSZane Shelley             o_list->push(std::make_shared<ConnectedCalloutResolution>(
2015d63cefcSZane Shelley                 std::get<0>(busData), std::get<1>(busData),
2025d63cefcSZane Shelley                 getPriority(priority), guard));
2037698b304SZane Shelley         }
2047698b304SZane Shelley         else if ("callout_bus" == type)
2057698b304SZane Shelley         {
2067698b304SZane Shelley             auto name     = a.at("name").get<std::string>();
207*4757a7bcSZane Shelley             auto priority = a.at("priority").get<std::string>();
2087698b304SZane Shelley             auto guard    = a.at("guard").get<bool>();
2097698b304SZane Shelley 
210*4757a7bcSZane Shelley             auto busData = parseBus(i_data, name);
211*4757a7bcSZane Shelley 
212*4757a7bcSZane Shelley             o_list->push(std::make_shared<BusCalloutResolution>(
213*4757a7bcSZane Shelley                 std::get<0>(busData), std::get<1>(busData),
214*4757a7bcSZane Shelley                 getPriority(priority), guard));
2157698b304SZane Shelley         }
2167698b304SZane Shelley         else if ("callout_clock" == type)
2177698b304SZane Shelley         {
21884721d90SZane Shelley             auto name     = a.at("name").get<std::string>();
2197698b304SZane Shelley             auto priority = a.at("priority").get<std::string>();
2207698b304SZane Shelley             auto guard    = a.at("guard").get<bool>();
2217698b304SZane Shelley 
22284721d90SZane Shelley             // clang-format off
22384721d90SZane Shelley             static const std::map<std::string, callout::ClockType> m =
22484721d90SZane Shelley             {
22584721d90SZane Shelley                 {"OSC_REF_CLOCK_0", callout::ClockType::OSC_REF_CLOCK_0},
22684721d90SZane Shelley                 {"OSC_REF_CLOCK_1", callout::ClockType::OSC_REF_CLOCK_1},
22784721d90SZane Shelley             };
22884721d90SZane Shelley             // clang-format on
22984721d90SZane Shelley 
23084721d90SZane Shelley             o_list->push(std::make_shared<ClockCalloutResolution>(
23184721d90SZane Shelley                 m.at(name), getPriority(priority), guard));
2327698b304SZane Shelley         }
2337698b304SZane Shelley         else if ("callout_procedure" == type)
2347698b304SZane Shelley         {
2357698b304SZane Shelley             auto name     = a.at("name").get<std::string>();
2367698b304SZane Shelley             auto priority = a.at("priority").get<std::string>();
2377698b304SZane Shelley 
2387698b304SZane Shelley             // clang-format off
239c85716caSZane Shelley             static const std::map<std::string, callout::Procedure> m =
2407698b304SZane Shelley             {
241c85716caSZane Shelley                 {"LEVEL2", callout::Procedure::NEXTLVL},
2427698b304SZane Shelley             };
2437698b304SZane Shelley             // clang-format on
2447698b304SZane Shelley 
2457698b304SZane Shelley             o_list->push(std::make_shared<ProcedureCalloutResolution>(
2467698b304SZane Shelley                 m.at(name), getPriority(priority)));
2477698b304SZane Shelley         }
2487698b304SZane Shelley         else if ("callout_part" == type)
2497698b304SZane Shelley         {
2507698b304SZane Shelley             auto name     = a.at("name").get<std::string>();
2517698b304SZane Shelley             auto priority = a.at("priority").get<std::string>();
2527698b304SZane Shelley 
2537698b304SZane Shelley             // TODO
2547698b304SZane Shelley             trace::inf("callout_part: name=%s priority=%s", name.c_str(),
2557698b304SZane Shelley                        priority.c_str());
2567698b304SZane Shelley         }
2577698b304SZane Shelley         else if ("plugin" == type)
2587698b304SZane Shelley         {
2597698b304SZane Shelley             auto name = a.at("name").get<std::string>();
2607698b304SZane Shelley 
2617698b304SZane Shelley             // TODO
2627698b304SZane Shelley             trace::inf("plugin: name=%s", name.c_str());
2637698b304SZane Shelley         }
2647698b304SZane Shelley         else
2657698b304SZane Shelley         {
2667698b304SZane Shelley             throw std::logic_error("Unsupported action type: " + type);
2677698b304SZane Shelley         }
2687698b304SZane Shelley     }
2697698b304SZane Shelley 
2707698b304SZane Shelley     // Done with this action pop it off the stack.
2717698b304SZane Shelley     stack.pop_back();
2727698b304SZane Shelley 
2737698b304SZane Shelley     return o_list;
2747698b304SZane Shelley }
2757698b304SZane Shelley 
2767698b304SZane Shelley //------------------------------------------------------------------------------
2777698b304SZane Shelley 
278c85716caSZane Shelley callout::Priority RasDataParser::getPriority(const std::string& i_priority)
2797698b304SZane Shelley {
2807698b304SZane Shelley     // clang-format off
281c85716caSZane Shelley     static const std::map<std::string, callout::Priority> m =
2827698b304SZane Shelley     {
283c85716caSZane Shelley         {"HIGH",  callout::Priority::HIGH},
284c85716caSZane Shelley         {"MED",   callout::Priority::MED},
285c85716caSZane Shelley         {"MED_A", callout::Priority::MED_A},
286c85716caSZane Shelley         {"MED_B", callout::Priority::MED_B},
287c85716caSZane Shelley         {"MED_C", callout::Priority::MED_C},
288c85716caSZane Shelley         {"LOW",   callout::Priority::LOW},
2897698b304SZane Shelley     };
2907698b304SZane Shelley     // clang-format on
2917698b304SZane Shelley 
2927698b304SZane Shelley     return m.at(i_priority);
2937698b304SZane Shelley }
2947698b304SZane Shelley 
2957698b304SZane Shelley //------------------------------------------------------------------------------
2967698b304SZane Shelley 
297a9b44344SZane Shelley } // namespace analyzer
298