xref: /openbmc/openpower-hw-diags/util/pdbg.cpp (revision 3f363d4a)
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 <util/pdbg.hpp>
13 #include <util/trace.hpp>
14 
15 #ifdef CONFIG_PHAL_API
16 #include <attributes_info.H>
17 #endif
18 
19 using namespace analyzer;
20 
21 namespace util
22 {
23 
24 namespace pdbg
25 {
26 
27 //------------------------------------------------------------------------------
28 
29 pdbg_target* getTrgt(const libhei::Chip& i_chip)
30 {
31     return (pdbg_target*)i_chip.getChip();
32 }
33 
34 //------------------------------------------------------------------------------
35 
36 pdbg_target* getTrgt(const std::string& i_path)
37 {
38     return pdbg_target_from_path(nullptr, i_path.c_str());
39 }
40 
41 //------------------------------------------------------------------------------
42 
43 const char* getPath(pdbg_target* i_trgt)
44 {
45     return pdbg_target_path(i_trgt);
46 }
47 
48 const char* getPath(const libhei::Chip& i_chip)
49 {
50     return getPath(getTrgt(i_chip));
51 }
52 
53 //------------------------------------------------------------------------------
54 
55 uint32_t getChipPos(pdbg_target* i_trgt)
56 {
57     uint32_t attr = 0;
58     pdbg_target_get_attribute(i_trgt, "ATTR_FAPI_POS", 4, 1, &attr);
59     return attr;
60 }
61 
62 uint32_t getChipPos(const libhei::Chip& i_chip)
63 {
64     return getChipPos(getTrgt(i_chip));
65 }
66 
67 //------------------------------------------------------------------------------
68 
69 uint8_t getUnitPos(pdbg_target* i_trgt)
70 {
71     uint8_t attr = 0;
72     pdbg_target_get_attribute(i_trgt, "ATTR_CHIP_UNIT_POS", 1, 1, &attr);
73     return attr;
74 }
75 
76 //------------------------------------------------------------------------------
77 
78 uint8_t getTrgtType(pdbg_target* i_trgt)
79 {
80     uint8_t attr = 0;
81     pdbg_target_get_attribute(i_trgt, "ATTR_TYPE", 1, 1, &attr);
82     return attr;
83 }
84 
85 uint8_t getTrgtType(const libhei::Chip& i_chip)
86 {
87     return getTrgtType(getTrgt(i_chip));
88 }
89 
90 //------------------------------------------------------------------------------
91 
92 pdbg_target* getParentChip(pdbg_target* i_unitTarget)
93 {
94     assert(nullptr != i_unitTarget);
95 
96     // Check if the given target is already a chip.
97     auto targetType = getTrgtType(i_unitTarget);
98     if (TYPE_PROC == targetType || TYPE_OCMB == targetType)
99     {
100         return i_unitTarget; // simply return the given target
101     }
102 
103     // Check if this unit is on an OCMB.
104     pdbg_target* parentChip = pdbg_target_parent("ocmb", i_unitTarget);
105 
106     // If not on the OCMB, check if this unit is on a PROC.
107     if (nullptr == parentChip)
108     {
109         parentChip = pdbg_target_parent("proc", i_unitTarget);
110     }
111 
112     // There should always be a parent chip. Throw an error if not found.
113     if (nullptr == parentChip)
114     {
115         throw std::logic_error("No parent chip found: i_unitTarget=" +
116                                std::string{getPath(i_unitTarget)});
117     }
118 
119     return parentChip;
120 }
121 
122 //------------------------------------------------------------------------------
123 
124 pdbg_target* getChipUnit(pdbg_target* i_parentChip, TargetType_t i_unitType,
125                          uint8_t i_unitPos)
126 {
127     assert(nullptr != i_parentChip);
128 
129     auto parentType = getTrgtType(i_parentChip);
130 
131     std::string devTreeType{};
132 
133     if (TYPE_PROC == parentType)
134     {
135         // clang-format off
136         static const std::map<TargetType_t, std::string> m =
137         {
138             {TYPE_MC,     "mc"      },
139             {TYPE_MCC,    "mcc"     },
140             {TYPE_OMI,    "omi"     },
141             {TYPE_OMIC,   "omic"    },
142             {TYPE_PAUC,   "pauc"    },
143             {TYPE_PAU,    "pau"     },
144             {TYPE_NMMU,   "nmmu"    },
145             {TYPE_IOHS,   "iohs"    },
146             {TYPE_IOLINK, "smpgroup"},
147             {TYPE_EQ,     "eq"      },
148             {TYPE_CORE,   "core"    },
149             {TYPE_PEC,    "pec"     },
150             {TYPE_PHB,    "phb"     },
151             {TYPE_NX,     "nx"      },
152         };
153         // clang-format on
154 
155         devTreeType = m.at(i_unitType);
156     }
157     else if (TYPE_OCMB == parentType)
158     {
159         // clang-format off
160         static const std::map<TargetType_t, std::string> m =
161         {
162             {TYPE_MEM_PORT, "mem_port"},
163         };
164         // clang-format on
165 
166         devTreeType = m.at(i_unitType);
167     }
168     else
169     {
170         throw std::logic_error("Unexpected parent chip: " +
171                                std::string{getPath(i_parentChip)});
172     }
173 
174     // Iterate all children of the parent and match the unit position.
175     pdbg_target* unitTarget = nullptr;
176     pdbg_for_each_target(devTreeType.c_str(), i_parentChip, unitTarget)
177     {
178         if (nullptr != unitTarget && i_unitPos == getUnitPos(unitTarget))
179         {
180             break; // found it
181         }
182     }
183 
184     // Print a warning if the target unit is not found, but don't throw an
185     // error.  Instead let the calling code deal with the it.
186     if (nullptr == unitTarget)
187     {
188         trace::err("No unit target found: i_parentChip=%s i_unitType=0x%02x "
189                    "i_unitPos=%u",
190                    getPath(i_parentChip), i_unitType, i_unitPos);
191     }
192 
193     return unitTarget;
194 }
195 
196 //------------------------------------------------------------------------------
197 
198 pdbg_target* getConnectedTarget(pdbg_target* i_rxTarget,
199                                 const callout::BusType& i_busType)
200 {
201     assert(nullptr != i_rxTarget);
202 
203     pdbg_target* txTarget = nullptr;
204 
205     auto rxType        = util::pdbg::getTrgtType(i_rxTarget);
206     std::string rxPath = util::pdbg::getPath(i_rxTarget);
207 
208     if (callout::BusType::SMP_BUS == i_busType &&
209         util::pdbg::TYPE_IOLINK == rxType)
210     {
211         // TODO: Will need to reference some sort of data that can tell us how
212         //       the processors are connected in the system. For now, return the
213         //       RX target to avoid returning a nullptr.
214         trace::inf("No support to get peer target on SMP bus");
215         txTarget = i_rxTarget;
216     }
217     else if (callout::BusType::SMP_BUS == i_busType &&
218              util::pdbg::TYPE_IOHS == rxType)
219     {
220         // TODO: Will need to reference some sort of data that can tell us how
221         //       the processors are connected in the system. For now, return the
222         //       RX target to avoid returning a nullptr.
223         trace::inf("No support to get peer target on SMP bus");
224         txTarget = i_rxTarget;
225     }
226     else if (callout::BusType::OMI_BUS == i_busType &&
227              util::pdbg::TYPE_OMI == rxType)
228     {
229         // This is a bit clunky. The pdbg APIs only give us the ability to
230         // iterate over the children instead of just returning a list. So we'll
231         // push all the children to a list and go from there.
232         std::vector<pdbg_target*> childList;
233 
234         pdbg_target* childTarget = nullptr;
235         pdbg_for_each_target("ocmb", i_rxTarget, childTarget)
236         {
237             if (nullptr != childTarget)
238             {
239                 childList.push_back(childTarget);
240             }
241         }
242 
243         // We know there should only be one OCMB per OMI.
244         if (1 != childList.size())
245         {
246             throw std::logic_error("Invalid child list size for " + rxPath);
247         }
248 
249         // Get the connected target.
250         txTarget = childList.front();
251     }
252     else if (callout::BusType::OMI_BUS == i_busType &&
253              util::pdbg::TYPE_OCMB == rxType)
254     {
255         txTarget = pdbg_target_parent("omi", i_rxTarget);
256         if (nullptr == txTarget)
257         {
258             throw std::logic_error("No parent OMI found for " + rxPath);
259         }
260     }
261     else
262     {
263         // This would be a code bug.
264         throw std::logic_error("Unsupported config: i_rxTarget=" + rxPath +
265                                " i_busType=" + i_busType.getString());
266     }
267 
268     assert(nullptr != txTarget); // just in case we missed something above
269 
270     return txTarget;
271 }
272 
273 //------------------------------------------------------------------------------
274 
275 pdbg_target* getPibTrgt(pdbg_target* i_procTrgt)
276 {
277     // The input target must be a processor.
278     assert(TYPE_PROC == getTrgtType(i_procTrgt));
279 
280     // Get the pib path.
281     char path[16];
282     sprintf(path, "/proc%d/pib", pdbg_target_index(i_procTrgt));
283 
284     // Return the pib target.
285     pdbg_target* pibTrgt = pdbg_target_from_path(nullptr, path);
286     assert(nullptr != pibTrgt);
287 
288     return pibTrgt;
289 }
290 
291 //------------------------------------------------------------------------------
292 
293 pdbg_target* getFsiTrgt(pdbg_target* i_procTrgt)
294 {
295     // The input target must be a processor.
296     assert(TYPE_PROC == getTrgtType(i_procTrgt));
297 
298     // Get the fsi path.
299     char path[16];
300     sprintf(path, "/proc%d/fsi", pdbg_target_index(i_procTrgt));
301 
302     // Return the fsi target.
303     pdbg_target* fsiTrgt = pdbg_target_from_path(nullptr, path);
304     assert(nullptr != fsiTrgt);
305 
306     return fsiTrgt;
307 }
308 
309 //------------------------------------------------------------------------------
310 
311 int getScom(pdbg_target* i_trgt, uint64_t i_addr, uint64_t& o_val)
312 {
313     // Only processor targets are supported.
314     // TODO: Will need to add OCMB support later.
315     assert(TYPE_PROC == getTrgtType(i_trgt));
316 
317     auto pibTrgt = util::pdbg::getPibTrgt(i_trgt);
318 
319     int rc = pib_read(pibTrgt, i_addr, &o_val);
320 
321     if (0 != rc)
322     {
323         trace::err("pib_read failure: target=%s addr=0x%0" PRIx64,
324                    util::pdbg::getPath(pibTrgt), i_addr);
325     }
326 
327     return rc;
328 }
329 
330 //------------------------------------------------------------------------------
331 
332 int getCfam(pdbg_target* i_trgt, uint32_t i_addr, uint32_t& o_val)
333 {
334     // Only processor targets are supported.
335     assert(TYPE_PROC == getTrgtType(i_trgt));
336 
337     auto fsiTrgt = util::pdbg::getFsiTrgt(i_trgt);
338 
339     int rc = fsi_read(fsiTrgt, i_addr, &o_val);
340 
341     if (0 != rc)
342     {
343         trace::err("fsi_read failure: target=%s addr=0x%08x",
344                    util::pdbg::getPath(fsiTrgt), i_addr);
345     }
346 
347     return rc;
348 }
349 
350 //------------------------------------------------------------------------------
351 
352 // IMPORTANT:
353 // The ATTR_CHIP_ID attribute will be synced from Hostboot to the BMC at some
354 // point during the IPL. It is possible that this information is needed before
355 // the sync occurs, in which case the value will return 0.
356 uint32_t __getChipId(pdbg_target* i_trgt)
357 {
358     uint32_t attr = 0;
359     pdbg_target_get_attribute(i_trgt, "ATTR_CHIP_ID", 4, 1, &attr);
360     return attr;
361 }
362 
363 // IMPORTANT:
364 // The ATTR_EC attribute will be synced from Hostboot to the BMC at some point
365 // during the IPL. It is possible that this information is needed before the
366 // sync occurs, in which case the value will return 0.
367 uint8_t __getChipEc(pdbg_target* i_trgt)
368 {
369     uint8_t attr = 0;
370     pdbg_target_get_attribute(i_trgt, "ATTR_EC", 1, 1, &attr);
371     return attr;
372 }
373 
374 uint32_t __getChipIdEc(pdbg_target* i_trgt)
375 {
376     auto chipId = __getChipId(i_trgt);
377     auto chipEc = __getChipEc(i_trgt);
378 
379     if (((0 == chipId) || (0 == chipEc)) && (TYPE_PROC == getTrgtType(i_trgt)))
380     {
381         // There is a special case where the model/level attributes have not
382         // been initialized in the devtree. This is possible on the epoch IPL
383         // where an attention occurs before Hostboot is able to update the
384         // devtree information on the BMC. It may is still possible to get this
385         // information from chips with CFAM access (i.e. a processor) via the
386         // CFAM chip ID register.
387 
388         uint32_t val = 0;
389         if (0 == getCfam(i_trgt, 0x100a, val))
390         {
391             chipId = ((val & 0x0F0FF000) >> 12);
392             chipEc = ((val & 0xF0000000) >> 24) | ((val & 0x00F00000) >> 20);
393         }
394     }
395 
396     return ((chipId & 0xffff) << 16) | (chipEc & 0xff);
397 }
398 
399 void __addChip(std::vector<libhei::Chip>& o_chips, pdbg_target* i_trgt,
400                libhei::ChipType_t i_type)
401 {
402     // Trace each chip for debug. It is important to show the type just in case
403     // the model/EC does not exist. See note below.
404     trace::inf("Chip found: type=0x%08" PRIx32 " chip=%s", i_type,
405                getPath(i_trgt));
406 
407     if (0 == i_type)
408     {
409         // This is a special case. See the details in __getChipIdEC(). There is
410         // nothing more we can do with this chip since we don't know what it is.
411         // So ignore the chip for now.
412     }
413     else
414     {
415         o_chips.emplace_back(i_trgt, i_type);
416     }
417 }
418 
419 void getActiveChips(std::vector<libhei::Chip>& o_chips)
420 {
421     o_chips.clear();
422 
423     // Iterate each processor.
424     pdbg_target* procTrgt;
425     pdbg_for_each_class_target("proc", procTrgt)
426     {
427         // We cannot use the proc target to determine if the chip is active.
428         // There is some design limitation in pdbg that requires the proc
429         // targets to always be active. Instead, we must get the associated pib
430         // target and check if it is active.
431 
432         // Active processors only.
433         if (PDBG_TARGET_ENABLED != pdbg_target_probe(getPibTrgt(procTrgt)))
434             continue;
435 
436         // Add the processor to the list.
437         __addChip(o_chips, procTrgt, __getChipIdEc(procTrgt));
438 
439         // Iterate the connected OCMBs, if they exist.
440         pdbg_target* ocmbTrgt;
441         pdbg_for_each_target("ocmb", procTrgt, ocmbTrgt)
442         {
443             // Active OCMBs only.
444             if (PDBG_TARGET_ENABLED != pdbg_target_probe(ocmbTrgt))
445                 continue;
446 
447             // Add the OCMB to the list.
448             __addChip(o_chips, ocmbTrgt, __getChipIdEc(ocmbTrgt));
449         }
450     }
451 }
452 
453 //------------------------------------------------------------------------------
454 
455 void getActiveProcessorChips(std::vector<pdbg_target*>& o_chips)
456 {
457     o_chips.clear();
458 
459     pdbg_target* procTrgt;
460     pdbg_for_each_class_target("proc", procTrgt)
461     {
462         // We cannot use the proc target to determine if the chip is active.
463         // There is some design limitation in pdbg that requires the proc
464         // targets to always be active. Instead, we must get the associated pib
465         // target and check if it is active.
466 
467         if (PDBG_TARGET_ENABLED != pdbg_target_probe(getPibTrgt(procTrgt)))
468             continue;
469 
470         o_chips.push_back(procTrgt);
471     }
472 }
473 
474 //------------------------------------------------------------------------------
475 
476 pdbg_target* getPrimaryProcessor()
477 {
478     // TODO: For at least P10, the primary processor (the one connected directly
479     //       to the BMC), will always be PROC 0. We will need to update this
480     //       later if we ever support an alternate primary processor.
481     return getTrgt("/proc0");
482 }
483 
484 //------------------------------------------------------------------------------
485 
486 bool queryHardwareAnalysisSupported()
487 {
488     // Hardware analysis is only supported on P10 systems and up.
489     return (PDBG_PROC_P9 < pdbg_get_proc());
490 }
491 
492 //------------------------------------------------------------------------------
493 
494 std::string getLocationCode(pdbg_target* trgt)
495 {
496     if (nullptr == trgt)
497     {
498         // Either the path is wrong or the attribute doesn't exist.
499         return std::string{};
500     }
501 
502 #ifdef CONFIG_PHAL_API
503 
504     ATTR_LOCATION_CODE_Type val;
505     if (DT_GET_PROP(ATTR_LOCATION_CODE, trgt, val))
506     {
507         // Get the immediate parent in the devtree path and try again.
508         return getLocationCode(pdbg_target_parent(nullptr, trgt));
509     }
510 
511     // Attribute found.
512     return std::string{val};
513 
514 #else
515 
516     return std::string{getPath(trgt)};
517 
518 #endif
519 }
520 
521 //------------------------------------------------------------------------------
522 
523 std::string getPhysDevPath(pdbg_target* trgt)
524 {
525     if (nullptr == trgt)
526     {
527         // Either the path is wrong or the attribute doesn't exist.
528         return std::string{};
529     }
530 
531 #ifdef CONFIG_PHAL_API
532 
533     ATTR_PHYS_DEV_PATH_Type val;
534     if (DT_GET_PROP(ATTR_PHYS_DEV_PATH, trgt, val))
535     {
536         // Get the immediate parent in the devtree path and try again.
537         return getPhysDevPath(pdbg_target_parent(nullptr, trgt));
538     }
539 
540     // Attribute found.
541     return std::string{val};
542 
543 #else
544 
545     return std::string{getPath(trgt)};
546 
547 #endif
548 }
549 
550 //------------------------------------------------------------------------------
551 
552 std::vector<uint8_t> getPhysBinPath(pdbg_target* target)
553 {
554     std::vector<uint8_t> binPath;
555 
556     if (nullptr != target)
557     {
558 #ifdef CONFIG_PHAL_API
559 
560         ATTR_PHYS_BIN_PATH_Type value;
561         if (DT_GET_PROP(ATTR_PHYS_BIN_PATH, target, value))
562         {
563             // The attrirbute for this target does not exist. Get the immediate
564             // parent in the devtree path and try again. Note that if there is
565             // no parent target, nullptr will be returned and that will be
566             // checked above.
567             return getPhysBinPath(pdbg_target_parent(nullptr, target));
568         }
569 
570         // Attribute was found. Copy the attribute array to the returned
571         // vector. Note that the reason we return the vector instead of just
572         // returning the array is because the array type and details only
573         // exists in this specific configuration.
574         binPath.insert(binPath.end(), value, value + sizeof(value));
575 
576 #endif
577     }
578 
579     return binPath;
580 }
581 
582 //------------------------------------------------------------------------------
583 
584 } // namespace pdbg
585 
586 } // namespace util
587