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