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 std::shared_ptr<Resolution>
16     RasDataParser::getResolution(const libhei::Signature& i_signature)
17 {
18     nlohmann::json data;
19 
20     try
21     {
22         data = iv_dataFiles.at(i_signature.getChip().getType());
23     }
24     catch (const std::out_of_range& e)
25     {
26         trace::err("No RAS data defined for chip type: 0x%08x",
27                    i_signature.getChip().getType());
28         throw; // caught later downstream
29     }
30 
31     const auto action = parseSignature(data, i_signature);
32 
33     std::shared_ptr<Resolution> resolution;
34 
35     try
36     {
37         resolution = parseAction(data, action);
38     }
39     catch (...)
40     {
41         trace::err("Unable to get resolution for action: %s", action.c_str());
42         throw; // caught later downstream
43     }
44 
45     return resolution;
46 }
47 
48 //------------------------------------------------------------------------------
49 
50 bool __checkActionForFlag(const std::string& i_action,
51                           const std::string& i_flag,
52                           const nlohmann::json& i_data)
53 {
54     bool o_isFlagSet = false;
55 
56     // Loop through the array of actions.
57     for (const auto& a : i_data.at("actions").at(i_action))
58     {
59         // Get the action type
60         auto type = a.at("type").get<std::string>();
61 
62         // If the action is another action, recursively call this function
63         if ("action" == type)
64         {
65             auto name   = a.at("name").get<std::string>();
66             o_isFlagSet = __checkActionForFlag(name, i_flag, i_data);
67             if (o_isFlagSet)
68             {
69                 break;
70             }
71         }
72         // If the action is a flag, check if it's the one
73         else if ("flag" == type)
74         {
75             auto name = a.at("name").get<std::string>();
76             if (name == i_flag)
77             {
78                 o_isFlagSet = true;
79                 break;
80             }
81         }
82     }
83 
84     return o_isFlagSet;
85 }
86 
87 //------------------------------------------------------------------------------
88 
89 bool RasDataParser::isFlagSet(const libhei::Signature& i_signature,
90                               const RasDataFlags i_flag) const
91 {
92     bool o_isFlagSet = false;
93 
94     // List of all flag enums mapping to their corresponding string
95     std::map<RasDataFlags, std::string> flagMap = {
96         {SUE_SOURCE, "sue_source"},
97         {SUE_SEEN, "sue_seen"},
98         {CS_POSSIBLE, "cs_possible"},
99         {RECOVERED_ERROR, "recovered_error"},
100         {INFORMATIONAL_ONLY, "informational_only"},
101         {MNFG_INFORMATIONAL_ONLY, "mnfg_informational_only"},
102         {MASK_BUT_DONT_CLEAR, "mask_but_dont_clear"},
103         {CRC_RELATED_ERR, "crc_related_err"},
104         {CRC_ROOT_CAUSE, "crc_root_cause"},
105         {ODP_DATA_CORRUPT_SIDE_EFFECT, "odp_data_corrupt_side_effect"},
106         {ODP_DATA_CORRUPT_ROOT_CAUSE, "odp_data_corrupt_root_cause"},
107     };
108     std::string strFlag = flagMap[i_flag];
109 
110     // If the input flag does not exist in the map, that's a code bug.
111     assert(0 != flagMap.count(i_flag));
112 
113     nlohmann::json data;
114     try
115     {
116         data = iv_dataFiles.at(i_signature.getChip().getType());
117     }
118     catch (const std::out_of_range& e)
119     {
120         trace::err("No RAS data defined for chip type: 0x%08x",
121                    i_signature.getChip().getType());
122         throw; // caught later downstream
123     }
124 
125     // Get the signature keys. All are hex (lower case) with no prefix.
126     char buf[5];
127     sprintf(buf, "%04x", i_signature.getId());
128     std::string id{buf};
129 
130     sprintf(buf, "%02x", i_signature.getBit());
131     std::string bit{buf};
132 
133     // Get the list of flags in string format from the data.
134     if (data.at("signatures").at(id).at(bit).contains("flags"))
135     {
136         auto flags = data.at("signatures")
137                          .at(id)
138                          .at(bit)
139                          .at("flags")
140                          .get<std::vector<std::string>>();
141 
142         // Check if the input flag exists
143         if (flags.end() != std::find(flags.begin(), flags.end(), strFlag))
144         {
145             o_isFlagSet = true;
146         }
147     }
148 
149     // If the flag hasn't been found, check if it was defined as part of the
150     // action for this input signature.
151     if (!o_isFlagSet)
152     {
153         const auto action = parseSignature(data, i_signature);
154         __checkActionForFlag(action, strFlag, data);
155     }
156 
157     return o_isFlagSet;
158 }
159 
160 //------------------------------------------------------------------------------
161 
162 unsigned int
163     RasDataParser::getVersion(const libhei::Signature& i_signature) const
164 {
165     unsigned int o_version = 0;
166 
167     nlohmann::json data;
168     try
169     {
170         data = iv_dataFiles.at(i_signature.getChip().getType());
171     }
172     catch (const std::out_of_range& e)
173     {
174         trace::err("No RAS data defined for chip type: 0x%08x",
175                    i_signature.getChip().getType());
176         throw; // caught later downstream
177     }
178 
179     o_version = data.at("version").get<unsigned int>();
180 
181     return o_version;
182 }
183 
184 //------------------------------------------------------------------------------
185 
186 void RasDataParser::initDataFiles()
187 {
188     iv_dataFiles.clear(); // initially empty
189 
190     // Get the RAS data schema files from the package `schema` subdirectory.
191     fs::path schemaDir{PACKAGE_DIR "schema"};
192     auto schemaRegex = R"(ras-data-schema-v[0-9]{2}\.json)";
193     std::vector<fs::path> schemaPaths;
194     util::findFiles(schemaDir, schemaRegex, schemaPaths);
195 
196     // Parse each of the schema files.
197     std::map<unsigned int, nlohmann::json> schemaFiles;
198     for (const auto& path : schemaPaths)
199     {
200         // Trace each data file for debug.
201         trace::inf("File found: path=%s", path.string().c_str());
202 
203         // Open the file.
204         std::ifstream file{path};
205         assert(file.good()); // The file must be readable.
206 
207         try
208         {
209             // Parse the JSON.
210             auto schema = nlohmann::json::parse(file);
211 
212             // Get the schema version.
213             auto version = schema.at("version").get<unsigned int>();
214 
215             // Keep track of the schemas.
216             auto ret = schemaFiles.emplace(version, schema);
217             assert(ret.second); // Should not have duplicate entries
218         }
219         catch (...)
220         {
221             trace::err("Failed to parse file: %s", path.string().c_str());
222             throw; // caught later downstream
223         }
224     }
225 
226     // Get the RAS data files from the package `data` subdirectory.
227     fs::path dataDir{PACKAGE_DIR "ras-data"};
228     std::vector<fs::path> dataPaths;
229     util::findFiles(dataDir, R"(.*\.json)", dataPaths);
230 
231     // Parse each of the data files.
232     for (const auto& path : dataPaths)
233     {
234         // Trace each data file for debug.
235         trace::inf("File found: path=%s", path.string().c_str());
236 
237         // Open the file.
238         std::ifstream file{path};
239         assert(file.good()); // The file must be readable.
240 
241         try
242         {
243             // Parse the JSON.
244             const auto data = nlohmann::json::parse(file);
245 
246             // Get the data version.
247             auto version = data.at("version").get<unsigned int>();
248 
249             // Get the schema for this file.
250             auto schema = schemaFiles.at(version);
251 
252             // Validate the data against the schema.
253             assert(util::validateJson(schema, data));
254 
255             // Get the chip model/EC level from the data. The value is currently
256             // stored as a string representation of the hex value. So it will
257             // have to be converted to an integer.
258             libhei::ChipType_t chipType =
259                 std::stoul(data.at("model_ec").get<std::string>(), 0, 16);
260 
261             // So far, so good. Add the entry.
262             auto ret = iv_dataFiles.emplace(chipType, data);
263             assert(ret.second); // Should not have duplicate entries
264         }
265         catch (...)
266         {
267             trace::err("Failed to parse file: %s", path.string().c_str());
268             throw; // caught later downstream
269         }
270     }
271 }
272 
273 //------------------------------------------------------------------------------
274 
275 std::string
276     RasDataParser::parseSignature(const nlohmann::json& i_data,
277                                   const libhei::Signature& i_signature) const
278 {
279     // Get the signature keys. All are hex (lower case) with no prefix.
280     char buf[5];
281     sprintf(buf, "%04x", i_signature.getId());
282     std::string id{buf};
283 
284     sprintf(buf, "%02x", i_signature.getBit());
285     std::string bit{buf};
286 
287     sprintf(buf, "%02x", i_signature.getInstance());
288     std::string inst{buf};
289 
290     std::string action;
291 
292     try
293     {
294         action =
295             i_data.at("signatures").at(id).at(bit).at(inst).get<std::string>();
296     }
297     catch (const std::out_of_range& e)
298     {
299         trace::err("No action defined for signature: %s %s %s", id.c_str(),
300                    bit.c_str(), inst.c_str());
301 
302         if (1 == i_data.at("version").get<unsigned int>())
303         {
304             throw; // caught later downstream
305         }
306         else
307         {
308             // Default to 'level2_M_th1' if no signature is found.
309             action = "level2_M_th1";
310         }
311     }
312 
313     // Return the action.
314     return action;
315 }
316 
317 //------------------------------------------------------------------------------
318 
319 std::tuple<callout::BusType, std::string>
320     RasDataParser::parseBus(const nlohmann::json& i_data,
321                             const std::string& i_name)
322 {
323     auto bus = i_data.at("buses").at(i_name);
324 
325     // clang-format off
326     static const std::map<std::string, callout::BusType> m =
327     {
328         {"SMP_BUS", callout::BusType::SMP_BUS},
329         {"OMI_BUS", callout::BusType::OMI_BUS},
330     };
331     // clang-format on
332 
333     auto busType = m.at(bus.at("type").get<std::string>());
334 
335     std::string unitPath{}; // default empty if unit does not exist
336     if (bus.contains("unit"))
337     {
338         auto unit = bus.at("unit").get<std::string>();
339         unitPath  = i_data.at("units").at(unit).get<std::string>();
340     }
341 
342     return std::make_tuple(busType, unitPath);
343 }
344 
345 //------------------------------------------------------------------------------
346 
347 std::shared_ptr<Resolution>
348     RasDataParser::parseAction(const nlohmann::json& i_data,
349                                const std::string& i_action)
350 {
351     auto o_list = std::make_shared<ResolutionList>();
352 
353     // This function will be called recursively and we want to prevent cyclic
354     // recursion.
355     static std::vector<std::string> stack;
356     assert(stack.end() == std::find(stack.begin(), stack.end(), i_action));
357     stack.push_back(i_action);
358 
359     // Iterate the action list and apply the changes.
360     for (const auto& a : i_data.at("actions").at(i_action))
361     {
362         auto type = a.at("type").get<std::string>();
363 
364         if ("action" == type)
365         {
366             auto name = a.at("name").get<std::string>();
367 
368             o_list->push(parseAction(i_data, name));
369         }
370         else if ("callout_self" == type)
371         {
372             auto priority = a.at("priority").get<std::string>();
373             auto guard    = a.at("guard").get<bool>();
374 
375             std::string path{}; // Must be empty to callout the chip.
376 
377             o_list->push(std::make_shared<HardwareCalloutResolution>(
378                 path, getPriority(priority), guard));
379         }
380         else if ("callout_unit" == type)
381         {
382             auto name     = a.at("name").get<std::string>();
383             auto priority = a.at("priority").get<std::string>();
384             auto guard    = a.at("guard").get<bool>();
385 
386             auto path = i_data.at("units").at(name).get<std::string>();
387 
388             o_list->push(std::make_shared<HardwareCalloutResolution>(
389                 path, getPriority(priority), guard));
390         }
391         else if ("callout_connected" == type)
392         {
393             auto name     = a.at("name").get<std::string>();
394             auto priority = a.at("priority").get<std::string>();
395             auto guard    = a.at("guard").get<bool>();
396 
397             auto busData = parseBus(i_data, name);
398 
399             o_list->push(std::make_shared<ConnectedCalloutResolution>(
400                 std::get<0>(busData), std::get<1>(busData),
401                 getPriority(priority), guard));
402         }
403         else if ("callout_bus" == type)
404         {
405             auto name     = a.at("name").get<std::string>();
406             auto priority = a.at("priority").get<std::string>();
407             auto guard    = a.at("guard").get<bool>();
408 
409             auto busData = parseBus(i_data, name);
410 
411             o_list->push(std::make_shared<BusCalloutResolution>(
412                 std::get<0>(busData), std::get<1>(busData),
413                 getPriority(priority), guard));
414         }
415         else if ("callout_clock" == type)
416         {
417             auto name     = a.at("name").get<std::string>();
418             auto priority = a.at("priority").get<std::string>();
419             auto guard    = a.at("guard").get<bool>();
420 
421             // clang-format off
422             static const std::map<std::string, callout::ClockType> m =
423             {
424                 {"OSC_REF_CLOCK_0", callout::ClockType::OSC_REF_CLOCK_0},
425                 {"OSC_REF_CLOCK_1", callout::ClockType::OSC_REF_CLOCK_1},
426                 {"TOD_CLOCK",       callout::ClockType::TOD_CLOCK},
427             };
428             // clang-format on
429 
430             o_list->push(std::make_shared<ClockCalloutResolution>(
431                 m.at(name), getPriority(priority), guard));
432         }
433         else if ("callout_procedure" == type)
434         {
435             auto name     = a.at("name").get<std::string>();
436             auto priority = a.at("priority").get<std::string>();
437 
438             // clang-format off
439             static const std::map<std::string, callout::Procedure> m =
440             {
441                 {"LEVEL2",   callout::Procedure::NEXTLVL},
442                 {"SUE_SEEN", callout::Procedure::SUE_SEEN},
443             };
444             // clang-format on
445 
446             o_list->push(std::make_shared<ProcedureCalloutResolution>(
447                 m.at(name), getPriority(priority)));
448         }
449         else if ("callout_part" == type)
450         {
451             auto name     = a.at("name").get<std::string>();
452             auto priority = a.at("priority").get<std::string>();
453 
454             // clang-format off
455             static const std::map<std::string, callout::PartType> m =
456             {
457                 {"PNOR", callout::PartType::PNOR},
458             };
459             // clang-format on
460 
461             o_list->push(std::make_shared<PartCalloutResolution>(
462                 m.at(name), getPriority(priority)));
463         }
464         else if ("plugin" == type)
465         {
466             auto name = a.at("name").get<std::string>();
467             auto inst = a.at("instance").get<unsigned int>();
468 
469             o_list->push(std::make_shared<PluginResolution>(name, inst));
470         }
471         else if ("flag" == type)
472         {
473             // No action, flags will be handled with the isFlagSet function
474         }
475         else
476         {
477             throw std::logic_error("Unsupported action type: " + type);
478         }
479     }
480 
481     // Done with this action pop it off the stack.
482     stack.pop_back();
483 
484     return o_list;
485 }
486 
487 //------------------------------------------------------------------------------
488 
489 callout::Priority RasDataParser::getPriority(const std::string& i_priority)
490 {
491     // clang-format off
492     static const std::map<std::string, callout::Priority> m =
493     {
494         {"HIGH",  callout::Priority::HIGH},
495         {"MED",   callout::Priority::MED},
496         {"MED_A", callout::Priority::MED_A},
497         {"MED_B", callout::Priority::MED_B},
498         {"MED_C", callout::Priority::MED_C},
499         {"LOW",   callout::Priority::LOW},
500     };
501     // clang-format on
502 
503     return m.at(i_priority);
504 }
505 
506 //------------------------------------------------------------------------------
507 
508 } // namespace analyzer
509