1 //------------------------------------------------------------------------------
2 // IMPORTANT:
3 // This file will be built in CI test and should work out-of-the-box in CI test
4 // with use of the fake device tree. Any functions that require addition support
5 // to simulate in CI test should be put in `pdbg_no_sim.cpp`.
6 //------------------------------------------------------------------------------
7
8 #include <assert.h>
9 #include <config.h>
10
11 #include <hei_main.hpp>
12 #include <nlohmann/json.hpp>
13 #include <util/dbus.hpp>
14 #include <util/pdbg.hpp>
15 #include <util/trace.hpp>
16
17 #include <filesystem>
18 #include <fstream>
19 #include <string>
20
21 #ifdef CONFIG_PHAL_API
22 #include <attributes_info.H>
23 #endif
24
25 using namespace analyzer;
26
27 namespace fs = std::filesystem;
28
29 namespace util
30 {
31
32 namespace pdbg
33 {
34
35 //------------------------------------------------------------------------------
36
getTrgt(const libhei::Chip & i_chip)37 pdbg_target* getTrgt(const libhei::Chip& i_chip)
38 {
39 return (pdbg_target*)i_chip.getChip();
40 }
41
42 //------------------------------------------------------------------------------
43
getTrgt(const std::string & i_path)44 pdbg_target* getTrgt(const std::string& i_path)
45 {
46 return pdbg_target_from_path(nullptr, i_path.c_str());
47 }
48
49 //------------------------------------------------------------------------------
50
getPath(pdbg_target * i_trgt)51 const char* getPath(pdbg_target* i_trgt)
52 {
53 return pdbg_target_path(i_trgt);
54 }
55
getPath(const libhei::Chip & i_chip)56 const char* getPath(const libhei::Chip& i_chip)
57 {
58 return getPath(getTrgt(i_chip));
59 }
60
61 //------------------------------------------------------------------------------
62
getChipPos(pdbg_target * i_trgt)63 uint32_t getChipPos(pdbg_target* i_trgt)
64 {
65 uint32_t attr = 0;
66 pdbg_target_get_attribute(i_trgt, "ATTR_FAPI_POS", 4, 1, &attr);
67 return attr;
68 }
69
getChipPos(const libhei::Chip & i_chip)70 uint32_t getChipPos(const libhei::Chip& i_chip)
71 {
72 return getChipPos(getTrgt(i_chip));
73 }
74
75 //------------------------------------------------------------------------------
76
getUnitPos(pdbg_target * i_trgt)77 uint8_t getUnitPos(pdbg_target* i_trgt)
78 {
79 uint8_t attr = 0;
80 pdbg_target_get_attribute(i_trgt, "ATTR_CHIP_UNIT_POS", 1, 1, &attr);
81 return attr;
82 }
83
84 //------------------------------------------------------------------------------
85
getTrgtType(pdbg_target * i_trgt)86 uint8_t getTrgtType(pdbg_target* i_trgt)
87 {
88 uint8_t attr = 0;
89 pdbg_target_get_attribute(i_trgt, "ATTR_TYPE", 1, 1, &attr);
90 return attr;
91 }
92
getTrgtType(const libhei::Chip & i_chip)93 uint8_t getTrgtType(const libhei::Chip& i_chip)
94 {
95 return getTrgtType(getTrgt(i_chip));
96 }
97
98 //------------------------------------------------------------------------------
99
getParentChip(pdbg_target * i_unitTarget)100 pdbg_target* getParentChip(pdbg_target* i_unitTarget)
101 {
102 assert(nullptr != i_unitTarget);
103
104 // Check if the given target is already a chip.
105 auto targetType = getTrgtType(i_unitTarget);
106 if (TYPE_PROC == targetType || TYPE_OCMB == targetType)
107 {
108 return i_unitTarget; // simply return the given target
109 }
110
111 // Check if this unit is on an OCMB.
112 pdbg_target* parentChip = pdbg_target_parent("ocmb", i_unitTarget);
113
114 // If not on the OCMB, check if this unit is on a PROC.
115 if (nullptr == parentChip)
116 {
117 parentChip = pdbg_target_parent("proc", i_unitTarget);
118 }
119
120 // There should always be a parent chip. Throw an error if not found.
121 if (nullptr == parentChip)
122 {
123 throw std::logic_error("No parent chip found: i_unitTarget=" +
124 std::string{getPath(i_unitTarget)});
125 }
126
127 return parentChip;
128 }
129
130 //------------------------------------------------------------------------------
131
getParentProcessor(pdbg_target * i_target)132 pdbg_target* getParentProcessor(pdbg_target* i_target)
133 {
134 assert(nullptr != i_target);
135
136 // Check if the given target is already a processor chip.
137 if (TYPE_PROC == getTrgtType(i_target))
138 {
139 return i_target; // simply return the given target
140 }
141
142 // Get the parent processor chip.
143 pdbg_target* parentChip = pdbg_target_parent("proc", i_target);
144
145 // There should always be a parent chip. Throw an error if not found.
146 if (nullptr == parentChip)
147 {
148 throw std::logic_error(
149 "No parent chip found: i_target=" + std::string{getPath(i_target)});
150 }
151
152 return parentChip;
153 }
154
155 //------------------------------------------------------------------------------
156
getChipUnit(pdbg_target * i_parentChip,TargetType_t i_unitType,uint8_t i_unitPos)157 pdbg_target* getChipUnit(pdbg_target* i_parentChip, TargetType_t i_unitType,
158 uint8_t i_unitPos)
159 {
160 assert(nullptr != i_parentChip);
161
162 auto parentType = getTrgtType(i_parentChip);
163
164 std::string devTreeType{};
165
166 if (TYPE_PROC == parentType)
167 {
168 // clang-format off
169 static const std::map<TargetType_t, std::string> m =
170 {
171 {TYPE_MC, "mc" },
172 {TYPE_MCC, "mcc" },
173 {TYPE_OMI, "omi" },
174 {TYPE_OMIC, "omic" },
175 {TYPE_PAUC, "pauc" },
176 {TYPE_PAU, "pau" },
177 {TYPE_NMMU, "nmmu" },
178 {TYPE_IOHS, "iohs" },
179 {TYPE_IOLINK, "smpgroup"},
180 {TYPE_EQ, "eq" },
181 {TYPE_CORE, "core" },
182 {TYPE_PEC, "pec" },
183 {TYPE_PHB, "phb" },
184 {TYPE_NX, "nx" },
185 };
186 // clang-format on
187
188 devTreeType = m.at(i_unitType);
189 }
190 else if (TYPE_OCMB == parentType)
191 {
192 // clang-format off
193 static const std::map<TargetType_t, std::string> m =
194 {
195 {TYPE_MEM_PORT, "mem_port"},
196 };
197 // clang-format on
198
199 devTreeType = m.at(i_unitType);
200 }
201 else
202 {
203 throw std::logic_error(
204 "Unexpected parent chip: " + std::string{getPath(i_parentChip)});
205 }
206
207 // Iterate all children of the parent and match the unit position.
208 pdbg_target* unitTarget = nullptr;
209 pdbg_for_each_target(devTreeType.c_str(), i_parentChip, unitTarget)
210 {
211 if (nullptr != unitTarget && i_unitPos == getUnitPos(unitTarget))
212 {
213 break; // found it
214 }
215 }
216
217 // Print a warning if the target unit is not found, but don't throw an
218 // error. Instead let the calling code deal with the it.
219 if (nullptr == unitTarget)
220 {
221 trace::err("No unit target found: i_parentChip=%s i_unitType=0x%02x "
222 "i_unitPos=%u",
223 getPath(i_parentChip), i_unitType, i_unitPos);
224 }
225
226 return unitTarget;
227 }
228
229 //------------------------------------------------------------------------------
230
getTargetAcrossBus(pdbg_target * i_rxTarget)231 pdbg_target* getTargetAcrossBus(pdbg_target* i_rxTarget)
232 {
233 assert(nullptr != i_rxTarget);
234
235 // Validate target type
236 auto rxType = util::pdbg::getTrgtType(i_rxTarget);
237 assert(util::pdbg::TYPE_IOLINK == rxType ||
238 util::pdbg::TYPE_IOHS == rxType);
239
240 pdbg_target* o_peerTarget;
241 fs::path filePath;
242
243 // Open the appropriate data file depending on machine type
244 util::dbus::MachineType machineType = util::dbus::getMachineType();
245 switch (machineType)
246 {
247 // Rainier/Blue Ridge 4U
248 case util::dbus::MachineType::Rainier_2S4U:
249 case util::dbus::MachineType::Rainier_1S4U:
250 case util::dbus::MachineType::BlueRidge_2S4U:
251 case util::dbus::MachineType::BlueRidge_1S4U:
252 filePath =
253 fs::path{PACKAGE_DIR "util-data/peer-targets-rainier-4u.json"};
254 break;
255 // Rainier/Blue Ridge 2U
256 case util::dbus::MachineType::Rainier_2S2U:
257 case util::dbus::MachineType::Rainier_1S2U:
258 case util::dbus::MachineType::BlueRidge_2S2U:
259 filePath =
260 fs::path{PACKAGE_DIR "util-data/peer-targets-rainier-2u.json"};
261 break;
262 // Everest/Fuji
263 case util::dbus::MachineType::Everest:
264 case util::dbus::MachineType::Fuji:
265 filePath =
266 fs::path{PACKAGE_DIR "util-data/peer-targets-everest.json"};
267 break;
268 // Bonnell/Balcones
269 case util::dbus::MachineType::Bonnell:
270 case util::dbus::MachineType::Balcones:
271 filePath =
272 fs::path{PACKAGE_DIR "util-data/peer-targets-bonnell.json"};
273 break;
274 default:
275 trace::err("Invalid machine type found %d",
276 static_cast<uint8_t>(machineType));
277 break;
278 }
279
280 std::ifstream file{filePath};
281 assert(file.good());
282
283 try
284 {
285 auto trgtMap = nlohmann::json::parse(file);
286 std::string rxPath = util::pdbg::getPath(i_rxTarget);
287 std::string peerPath = trgtMap.at(rxPath).get<std::string>();
288
289 o_peerTarget = util::pdbg::getTrgt(peerPath);
290 }
291 catch (...)
292 {
293 trace::err("Failed to parse file: %s", filePath.string().c_str());
294 throw;
295 }
296
297 return o_peerTarget;
298 }
299
300 //------------------------------------------------------------------------------
301
getConnectedTarget(pdbg_target * i_rxTarget,const callout::BusType & i_busType)302 pdbg_target* getConnectedTarget(pdbg_target* i_rxTarget,
303 const callout::BusType& i_busType)
304 {
305 assert(nullptr != i_rxTarget);
306
307 pdbg_target* txTarget = nullptr;
308
309 auto rxType = util::pdbg::getTrgtType(i_rxTarget);
310 std::string rxPath = util::pdbg::getPath(i_rxTarget);
311
312 if (callout::BusType::SMP_BUS == i_busType &&
313 util::pdbg::TYPE_IOLINK == rxType)
314 {
315 txTarget = getTargetAcrossBus(i_rxTarget);
316 }
317 else if (callout::BusType::SMP_BUS == i_busType &&
318 util::pdbg::TYPE_IOHS == rxType)
319 {
320 txTarget = getTargetAcrossBus(i_rxTarget);
321 }
322 else if (callout::BusType::OMI_BUS == i_busType &&
323 util::pdbg::TYPE_OMI == rxType)
324 {
325 // This is a bit clunky. The pdbg APIs only give us the ability to
326 // iterate over the children instead of just returning a list. So
327 // we'll push all the children to a list and go from there.
328 std::vector<pdbg_target*> childList;
329
330 pdbg_target* childTarget = nullptr;
331 pdbg_for_each_target("ocmb", i_rxTarget, childTarget)
332 {
333 if (nullptr != childTarget)
334 {
335 childList.push_back(childTarget);
336 }
337 }
338
339 // We know there should only be one OCMB per OMI.
340 if (1 != childList.size())
341 {
342 throw std::logic_error("Invalid child list size for " + rxPath);
343 }
344
345 // Get the connected target.
346 txTarget = childList.front();
347 }
348 else if (callout::BusType::OMI_BUS == i_busType &&
349 util::pdbg::TYPE_OCMB == rxType)
350 {
351 txTarget = pdbg_target_parent("omi", i_rxTarget);
352 if (nullptr == txTarget)
353 {
354 throw std::logic_error("No parent OMI found for " + rxPath);
355 }
356 }
357 else
358 {
359 // This would be a code bug.
360 throw std::logic_error("Unsupported config: i_rxTarget=" + rxPath +
361 " i_busType=" + i_busType.getString());
362 }
363
364 assert(nullptr != txTarget); // just in case we missed something above
365
366 return txTarget;
367 }
368
369 //------------------------------------------------------------------------------
370
getPibTrgt(pdbg_target * i_procTrgt)371 pdbg_target* getPibTrgt(pdbg_target* i_procTrgt)
372 {
373 // The input target must be a processor.
374 assert(TYPE_PROC == getTrgtType(i_procTrgt));
375
376 // Get the pib path.
377 char path[16];
378 sprintf(path, "/proc%d/pib", pdbg_target_index(i_procTrgt));
379
380 // Return the pib target.
381 pdbg_target* pibTrgt = pdbg_target_from_path(nullptr, path);
382 assert(nullptr != pibTrgt);
383
384 return pibTrgt;
385 }
386
387 //------------------------------------------------------------------------------
388
getFsiTrgt(pdbg_target * i_procTrgt)389 pdbg_target* getFsiTrgt(pdbg_target* i_procTrgt)
390 {
391 // The input target must be a processor.
392 assert(TYPE_PROC == getTrgtType(i_procTrgt));
393
394 // Get the fsi path.
395 char path[16];
396 sprintf(path, "/proc%d/fsi", pdbg_target_index(i_procTrgt));
397
398 // Return the fsi target.
399 pdbg_target* fsiTrgt = pdbg_target_from_path(nullptr, path);
400 assert(nullptr != fsiTrgt);
401
402 return fsiTrgt;
403 }
404
405 //------------------------------------------------------------------------------
406
407 // IMPORTANT:
408 // The ATTR_CHIP_ID attribute will be synced from Hostboot to the BMC at
409 // some point during the IPL. It is possible that this information is needed
410 // before the sync occurs, in which case the value will return 0.
__getChipId(pdbg_target * i_trgt)411 uint32_t __getChipId(pdbg_target* i_trgt)
412 {
413 uint32_t attr = 0;
414 pdbg_target_get_attribute(i_trgt, "ATTR_CHIP_ID", 4, 1, &attr);
415 return attr;
416 }
417
418 // IMPORTANT:
419 // The ATTR_EC attribute will be synced from Hostboot to the BMC at some
420 // point during the IPL. It is possible that this information is needed
421 // before the sync occurs, in which case the value will return 0.
__getChipEc(pdbg_target * i_trgt)422 uint8_t __getChipEc(pdbg_target* i_trgt)
423 {
424 uint8_t attr = 0;
425 pdbg_target_get_attribute(i_trgt, "ATTR_EC", 1, 1, &attr);
426 return attr;
427 }
428
__getChipIdEc(pdbg_target * i_trgt)429 uint32_t __getChipIdEc(pdbg_target* i_trgt)
430 {
431 auto chipId = __getChipId(i_trgt);
432 auto chipEc = __getChipEc(i_trgt);
433
434 if (((0 == chipId) || (0 == chipEc)) && (TYPE_PROC == getTrgtType(i_trgt)))
435 {
436 // There is a special case where the model/level attributes have not
437 // been initialized in the devtree. This is possible on the epoch
438 // IPL where an attention occurs before Hostboot is able to update
439 // the devtree information on the BMC. It may is still possible to
440 // get this information from chips with CFAM access (i.e. a
441 // processor) via the CFAM chip ID register.
442
443 uint32_t val = 0;
444 if (0 == getCfam(i_trgt, 0x100a, val))
445 {
446 chipId = ((val & 0x0F0FF000) >> 12);
447 chipEc = ((val & 0xF0000000) >> 24) | ((val & 0x00F00000) >> 20);
448 }
449 }
450
451 return ((chipId & 0xffff) << 16) | (chipEc & 0xff);
452 }
453
__addChip(std::vector<libhei::Chip> & o_chips,pdbg_target * i_trgt,libhei::ChipType_t i_type)454 void __addChip(std::vector<libhei::Chip>& o_chips, pdbg_target* i_trgt,
455 libhei::ChipType_t i_type)
456 {
457 // Trace each chip for debug. It is important to show the type just in
458 // case the model/EC does not exist. See note below.
459 trace::inf("Chip found: type=0x%08" PRIx32 " chip=%s", i_type,
460 getPath(i_trgt));
461
462 if (0 == i_type)
463 {
464 // This is a special case. See the details in __getChipIdEC(). There
465 // is nothing more we can do with this chip since we don't know what
466 // it is. So ignore the chip for now.
467 }
468 else
469 {
470 o_chips.emplace_back(i_trgt, i_type);
471 }
472 }
473
474 // Should ignore OCMBs that have been masked on the processor side of the bus.
__isMaskedOcmb(const libhei::Chip & i_chip)475 bool __isMaskedOcmb(const libhei::Chip& i_chip)
476 {
477 // TODO: This function only works for P10 processors will need to update for
478 // subsequent chips.
479
480 // Map of MCC target position to DSTL_FIR_MASK address.
481 static const std::map<unsigned int, uint64_t> addrs = {
482 {0, 0x0C010D03}, {1, 0x0C010D43}, {2, 0x0D010D03}, {3, 0x0D010D43},
483 {4, 0x0E010D03}, {5, 0x0E010D43}, {6, 0x0F010D03}, {7, 0x0F010D43},
484 };
485
486 auto ocmb = getTrgt(i_chip);
487
488 // Confirm this chip is an OCMB.
489 if (TYPE_OCMB != getTrgtType(ocmb))
490 {
491 return false;
492 }
493
494 // Get the connected MCC target on the processor chip.
495 auto mcc = pdbg_target_parent("mcc", ocmb);
496 if (nullptr == mcc)
497 {
498 throw std::logic_error(
499 "No parent MCC found for " + std::string{getPath(ocmb)});
500 }
501
502 // Read the associated DSTL_FIR_MASK.
503 uint64_t val = 0;
504 if (getScom(getParentChip(mcc), addrs.at(getUnitPos(mcc)), val))
505 {
506 // Just let this go. The SCOM code will log the error.
507 return false;
508 }
509
510 // The DSTL_FIR has bits for each of the two memory channels on the MCC.
511 auto chnlPos = getChipPos(ocmb) % 2;
512
513 // Channel 0 => bits 0-3, channel 1 => bits 4-7.
514 auto mask = (val >> (60 - (4 * chnlPos))) & 0xf;
515
516 // Return true if the mask is set to all 1's.
517 if (0xf == mask)
518 {
519 trace::inf("OCMB masked on processor side of bus: %s", getPath(ocmb));
520 return true;
521 }
522
523 return false; // default
524 }
525
getActiveChips(std::vector<libhei::Chip> & o_chips)526 void getActiveChips(std::vector<libhei::Chip>& o_chips)
527 {
528 o_chips.clear();
529
530 // Iterate each processor.
531 pdbg_target* procTrgt;
532 pdbg_for_each_class_target("proc", procTrgt)
533 {
534 // We cannot use the proc target to determine if the chip is active.
535 // There is some design limitation in pdbg that requires the proc
536 // targets to always be active. Instead, we must get the associated
537 // pib target and check if it is active.
538
539 // Active processors only.
540 if (PDBG_TARGET_ENABLED != pdbg_target_probe(getPibTrgt(procTrgt)))
541 continue;
542
543 // Add the processor to the list.
544 __addChip(o_chips, procTrgt, __getChipIdEc(procTrgt));
545
546 // Iterate the connected OCMBs, if they exist.
547 pdbg_target* ocmbTrgt;
548 pdbg_for_each_target("ocmb", procTrgt, ocmbTrgt)
549 {
550 // Active OCMBs only.
551 if (PDBG_TARGET_ENABLED != pdbg_target_probe(ocmbTrgt))
552 continue;
553
554 // Add the OCMB to the list.
555 __addChip(o_chips, ocmbTrgt, __getChipIdEc(ocmbTrgt));
556 }
557 }
558
559 // Ignore OCMBs that have been masked on the processor side of the bus.
560 o_chips.erase(
561 std::remove_if(o_chips.begin(), o_chips.end(), __isMaskedOcmb),
562 o_chips.end());
563 }
564
565 //------------------------------------------------------------------------------
566
getActiveProcessorChips(std::vector<pdbg_target * > & o_chips)567 void getActiveProcessorChips(std::vector<pdbg_target*>& o_chips)
568 {
569 o_chips.clear();
570
571 pdbg_target* procTrgt;
572 pdbg_for_each_class_target("proc", procTrgt)
573 {
574 // We cannot use the proc target to determine if the chip is active.
575 // There is some design limitation in pdbg that requires the proc
576 // targets to always be active. Instead, we must get the associated pib
577 // target and check if it is active.
578
579 if (PDBG_TARGET_ENABLED != pdbg_target_probe(getPibTrgt(procTrgt)))
580 continue;
581
582 o_chips.push_back(procTrgt);
583 }
584 }
585
586 //------------------------------------------------------------------------------
587
getPrimaryProcessor()588 pdbg_target* getPrimaryProcessor()
589 {
590 // TODO: For at least P10, the primary processor (the one connected
591 // directly
592 // to the BMC), will always be PROC 0. We will need to update this
593 // later if we ever support an alternate primary processor.
594 return getTrgt("/proc0");
595 }
596
597 //------------------------------------------------------------------------------
598
queryHardwareAnalysisSupported()599 bool queryHardwareAnalysisSupported()
600 {
601 // Hardware analysis is only supported on P10 systems and up.
602 return (PDBG_PROC_P9 < pdbg_get_proc());
603 }
604
605 //------------------------------------------------------------------------------
606
getLocationCode(pdbg_target * trgt)607 std::string getLocationCode(pdbg_target* trgt)
608 {
609 if (nullptr == trgt)
610 {
611 // Either the path is wrong or the attribute doesn't exist.
612 return std::string{};
613 }
614
615 #ifdef CONFIG_PHAL_API
616
617 ATTR_LOCATION_CODE_Type val;
618 if (DT_GET_PROP(ATTR_LOCATION_CODE, trgt, val))
619 {
620 // Get the immediate parent in the devtree path and try again.
621 return getLocationCode(pdbg_target_parent(nullptr, trgt));
622 }
623
624 // Attribute found.
625 return std::string{val};
626
627 #else
628
629 return std::string{getPath(trgt)};
630
631 #endif
632 }
633
634 //------------------------------------------------------------------------------
635
getPhysDevPath(pdbg_target * trgt)636 std::string getPhysDevPath(pdbg_target* trgt)
637 {
638 if (nullptr == trgt)
639 {
640 // Either the path is wrong or the attribute doesn't exist.
641 return std::string{};
642 }
643
644 #ifdef CONFIG_PHAL_API
645
646 ATTR_PHYS_DEV_PATH_Type val;
647 if (DT_GET_PROP(ATTR_PHYS_DEV_PATH, trgt, val))
648 {
649 // Get the immediate parent in the devtree path and try again.
650 return getPhysDevPath(pdbg_target_parent(nullptr, trgt));
651 }
652
653 // Attribute found.
654 return std::string{val};
655
656 #else
657
658 return std::string{getPath(trgt)};
659
660 #endif
661 }
662
663 //------------------------------------------------------------------------------
664
getPhysBinPath(pdbg_target * target)665 std::vector<uint8_t> getPhysBinPath(pdbg_target* target)
666 {
667 std::vector<uint8_t> binPath;
668
669 if (nullptr != target)
670 {
671 #ifdef CONFIG_PHAL_API
672
673 ATTR_PHYS_BIN_PATH_Type value;
674 if (DT_GET_PROP(ATTR_PHYS_BIN_PATH, target, value))
675 {
676 // The attrirbute for this target does not exist. Get the
677 // immediate parent in the devtree path and try again. Note that
678 // if there is no parent target, nullptr will be returned and
679 // that will be checked above.
680 return getPhysBinPath(pdbg_target_parent(nullptr, target));
681 }
682
683 // Attribute was found. Copy the attribute array to the returned
684 // vector. Note that the reason we return the vector instead of just
685 // returning the array is because the array type and details only
686 // exists in this specific configuration.
687 binPath.insert(binPath.end(), value, value + sizeof(value));
688
689 #endif
690 }
691
692 return binPath;
693 }
694
695 //------------------------------------------------------------------------------
696
697 } // namespace pdbg
698
699 } // namespace util
700