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             };
298             // clang-format on
299 
300             o_list->push(std::make_shared<ProcedureCalloutResolution>(
301                 m.at(name), getPriority(priority)));
302         }
303         else if ("callout_part" == type)
304         {
305             auto name     = a.at("name").get<std::string>();
306             auto priority = a.at("priority").get<std::string>();
307 
308             // clang-format off
309             static const std::map<std::string, callout::PartType> m =
310             {
311                 {"PNOR", callout::PartType::PNOR},
312             };
313             // clang-format on
314 
315             o_list->push(std::make_shared<PartCalloutResolution>(
316                 m.at(name), getPriority(priority)));
317         }
318         else if ("plugin" == type)
319         {
320             auto name = a.at("name").get<std::string>();
321             auto inst = a.at("instance").get<unsigned int>();
322 
323             o_list->push(std::make_shared<PluginResolution>(name, inst));
324         }
325         else
326         {
327             throw std::logic_error("Unsupported action type: " + type);
328         }
329     }
330 
331     // Done with this action pop it off the stack.
332     stack.pop_back();
333 
334     return o_list;
335 }
336 
337 //------------------------------------------------------------------------------
338 
339 callout::Priority RasDataParser::getPriority(const std::string& i_priority)
340 {
341     // clang-format off
342     static const std::map<std::string, callout::Priority> m =
343     {
344         {"HIGH",  callout::Priority::HIGH},
345         {"MED",   callout::Priority::MED},
346         {"MED_A", callout::Priority::MED_A},
347         {"MED_B", callout::Priority::MED_B},
348         {"MED_C", callout::Priority::MED_C},
349         {"LOW",   callout::Priority::LOW},
350     };
351     // clang-format on
352 
353     return m.at(i_priority);
354 }
355 
356 //------------------------------------------------------------------------------
357 
358 } // namespace analyzer
359