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