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