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