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