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 
parseSignature(const nlohmann::json & i_data,const libhei::Signature & i_signature) const294 std::string RasDataParser::parseSignature(
295     const nlohmann::json& i_data, const libhei::Signature& i_signature) const
296 {
297     // Get the signature keys. All are hex (lower case) with no prefix.
298     char buf[5];
299     sprintf(buf, "%04x", i_signature.getId());
300     std::string id{buf};
301 
302     sprintf(buf, "%02x", i_signature.getBit());
303     std::string bit{buf};
304 
305     sprintf(buf, "%02x", i_signature.getInstance());
306     std::string inst{buf};
307 
308     std::string action;
309 
310     try
311     {
312         action =
313             i_data.at("signatures").at(id).at(bit).at(inst).get<std::string>();
314     }
315     catch (const nlohmann::json::out_of_range& e)
316     {
317         trace::err("No action defined for signature: %s %s %s", id.c_str(),
318                    bit.c_str(), inst.c_str());
319 
320         // Default to 'level2_M_th1' if no signature is found.
321         action = "level2_M_th1";
322     }
323 
324     // Return the action.
325     return action;
326 }
327 
328 //------------------------------------------------------------------------------
329 
parseBus(const nlohmann::json & i_data,const std::string & i_name)330 std::tuple<callout::BusType, std::string> RasDataParser::parseBus(
331     const nlohmann::json& i_data, const std::string& i_name)
332 {
333     auto bus = i_data.at("buses").at(i_name);
334 
335     // clang-format off
336     static const std::map<std::string, callout::BusType> m =
337     {
338         {"SMP_BUS", callout::BusType::SMP_BUS},
339         {"OMI_BUS", callout::BusType::OMI_BUS},
340     };
341     // clang-format on
342 
343     auto busType = m.at(bus.at("type").get<std::string>());
344 
345     std::string unitPath{}; // default empty if unit does not exist
346     if (bus.contains("unit"))
347     {
348         auto unit = bus.at("unit").get<std::string>();
349         unitPath = i_data.at("units").at(unit).get<std::string>();
350     }
351 
352     return std::make_tuple(busType, unitPath);
353 }
354 
355 //------------------------------------------------------------------------------
356 
parseAction(const nlohmann::json & i_data,const std::string & i_action)357 std::shared_ptr<Resolution> RasDataParser::parseAction(
358     const nlohmann::json& i_data, const std::string& i_action)
359 {
360     auto o_list = std::make_shared<ResolutionList>();
361 
362     // This function will be called recursively and we want to prevent cyclic
363     // recursion.
364     static std::vector<std::string> stack;
365     assert(stack.end() == std::find(stack.begin(), stack.end(), i_action));
366     stack.push_back(i_action);
367 
368     // Iterate the action list and apply the changes.
369     for (const auto& a : i_data.at("actions").at(i_action))
370     {
371         auto type = a.at("type").get<std::string>();
372 
373         if ("action" == type)
374         {
375             auto name = a.at("name").get<std::string>();
376 
377             o_list->push(parseAction(i_data, name));
378         }
379         else if ("callout_self" == type)
380         {
381             auto priority = a.at("priority").get<std::string>();
382             auto guard = a.at("guard").get<bool>();
383 
384             std::string path{}; // Must be empty to callout the chip.
385 
386             o_list->push(std::make_shared<HardwareCalloutResolution>(
387                 path, getPriority(priority), guard));
388         }
389         else if ("callout_unit" == type)
390         {
391             auto name = a.at("name").get<std::string>();
392             auto priority = a.at("priority").get<std::string>();
393             auto guard = a.at("guard").get<bool>();
394 
395             auto path = i_data.at("units").at(name).get<std::string>();
396 
397             o_list->push(std::make_shared<HardwareCalloutResolution>(
398                 path, getPriority(priority), guard));
399         }
400         else if ("callout_connected" == type)
401         {
402             auto name = a.at("name").get<std::string>();
403             auto priority = a.at("priority").get<std::string>();
404             auto guard = a.at("guard").get<bool>();
405 
406             auto busData = parseBus(i_data, name);
407 
408             o_list->push(std::make_shared<ConnectedCalloutResolution>(
409                 std::get<0>(busData), std::get<1>(busData),
410                 getPriority(priority), guard));
411         }
412         else if ("callout_bus" == type)
413         {
414             auto name = a.at("name").get<std::string>();
415             auto priority = a.at("priority").get<std::string>();
416             auto guard = a.at("guard").get<bool>();
417 
418             auto busData = parseBus(i_data, name);
419 
420             o_list->push(std::make_shared<BusCalloutResolution>(
421                 std::get<0>(busData), std::get<1>(busData),
422                 getPriority(priority), guard));
423         }
424         else if ("callout_clock" == type)
425         {
426             auto name = a.at("name").get<std::string>();
427             auto priority = a.at("priority").get<std::string>();
428             auto guard = a.at("guard").get<bool>();
429 
430             // clang-format off
431             static const std::map<std::string, callout::ClockType> m =
432             {
433                 {"OSC_REF_CLOCK_0", callout::ClockType::OSC_REF_CLOCK_0},
434                 {"OSC_REF_CLOCK_1", callout::ClockType::OSC_REF_CLOCK_1},
435                 {"TOD_CLOCK",       callout::ClockType::TOD_CLOCK},
436             };
437             // clang-format on
438 
439             o_list->push(std::make_shared<ClockCalloutResolution>(
440                 m.at(name), getPriority(priority), guard));
441         }
442         else if ("callout_procedure" == type)
443         {
444             auto name = a.at("name").get<std::string>();
445             auto priority = a.at("priority").get<std::string>();
446 
447             // clang-format off
448             static const std::map<std::string, callout::Procedure> m =
449             {
450                 {"LEVEL2",   callout::Procedure::NEXTLVL},
451                 {"SUE_SEEN", callout::Procedure::SUE_SEEN},
452             };
453             // clang-format on
454 
455             o_list->push(std::make_shared<ProcedureCalloutResolution>(
456                 m.at(name), getPriority(priority)));
457         }
458         else if ("callout_part" == type)
459         {
460             auto name = a.at("name").get<std::string>();
461             auto priority = a.at("priority").get<std::string>();
462 
463             // clang-format off
464             static const std::map<std::string, callout::PartType> m =
465             {
466                 {"PNOR", callout::PartType::PNOR},
467             };
468             // clang-format on
469 
470             o_list->push(std::make_shared<PartCalloutResolution>(
471                 m.at(name), getPriority(priority)));
472         }
473         else if ("plugin" == type)
474         {
475             auto name = a.at("name").get<std::string>();
476             auto inst = a.at("instance").get<unsigned int>();
477 
478             o_list->push(std::make_shared<PluginResolution>(name, inst));
479         }
480         else if ("flag" == type)
481         {
482             // No action, flags will be handled with the isFlagSet function
483         }
484         else
485         {
486             throw std::logic_error("Unsupported action type: " + type);
487         }
488     }
489 
490     // Done with this action pop it off the stack.
491     stack.pop_back();
492 
493     return o_list;
494 }
495 
496 //------------------------------------------------------------------------------
497 
getPriority(const std::string & i_priority)498 callout::Priority RasDataParser::getPriority(const std::string& i_priority)
499 {
500     // clang-format off
501     static const std::map<std::string, callout::Priority> m =
502     {
503         {"HIGH",  callout::Priority::HIGH},
504         {"MED",   callout::Priority::MED},
505         {"MED_A", callout::Priority::MED_A},
506         {"MED_B", callout::Priority::MED_B},
507         {"MED_C", callout::Priority::MED_C},
508         {"LOW",   callout::Priority::LOW},
509     };
510     // clang-format on
511 
512     return m.at(i_priority);
513 }
514 
515 //------------------------------------------------------------------------------
516 
517 } // namespace analyzer
518