From da08e7a278bc2573c9d17ddc69de2c94a4cf8018 Mon Sep 17 00:00:00 2001 From: Al Grant Date: Fri, 17 Jan 2025 18:16:35 +0000 Subject: [PATCH] #25: fix reading device tree nodes --- coresight-tools/cs_topology.py | 41 ++++++++++++++++++++----- coresight-tools/cs_topology_sysfs.py | 46 ++++++++++++++++++++++++---- 2 files changed, 74 insertions(+), 13 deletions(-) diff --git a/coresight-tools/cs_topology.py b/coresight-tools/cs_topology.py index 7a2dbe5..499530c 100644 --- a/coresight-tools/cs_topology.py +++ b/coresight-tools/cs_topology.py @@ -125,7 +125,7 @@ def __init__(self, platform, device_type, is_hidden=False, name=None, type_name= self.part_vendor = None self.part_number = None # e.g. 0x906 self.cpu_number = None # CPU (PE) number within the system, e.g. as seen by Linux - self.affine_cpu = None + self.affine_cpu = None # a CS_DEVTYPE_CORE Device object we are affine to self.type = device_type if device_type == CS_DEVTYPE_CORE: self.affine_cpu = self @@ -156,13 +156,26 @@ def type_str(self): return str(self.type) def __str__(self): + """ + Generate a string representation for the device, including its name (if known) + and base address. This could unfortunately duplicate the address, if the naming + convention includes the address, e.g. + etm2@0x80000 + but + etm@0x0080000@0x80000 + So we try to be clever and only append the base address if it's not already + in the name. + """ s = "" if self.name is not None: s = self.name else: s += "<%s>" % self.type_str() if self.is_memory_mapped(): - s += "@" + self.address_str() + if self.name is not None and '@' in self.name: + pass + else: + s += "@" + self.address_str() return s def links(self, type, end=None): @@ -208,7 +221,7 @@ def set_affine_cpu(self, cpu_dev): """ if self.affine_cpu != cpu_dev: assert self.affine_cpu is None, "set_affine_cpu() called twice with different devices" - assert cpu_dev.type == CS_DEVTYPE_CORE + assert cpu_dev.type == CS_DEVTYPE_CORE, "set_affine_cpu needs a core device: %s" % cpu_dev self.affine_cpu = cpu_dev cpu_dev.affine_devices.append(self) if self.cpu_number is None: @@ -218,9 +231,22 @@ def set_cpu_number(self, n): self.cpu_number = n if n > self.platform.max_cpu_number: self.platform.max_cpu_number = n - if type == CS_DEVTYPE_CORE: + if self.type == CS_DEVTYPE_CORE: + # Core cpu number is now known, so update all its affine devices + # to get the same CPU number. for d in self.affine_devices: + assert d.affine_cpu == self d.cpu_number = n + # Some devices may already have got a CPU number, but because the core + # has only just got its own number, they're not tied together. + # Do that now. + for d in self.platform.devices: + if d.cpu_number == n and d.affine_cpu is not self: + d.set_affine_cpu(self) + elif self.affine_cpu is None: + cpu_dev = self.platform.device_by_cpu(n) + if cpu_dev is not None: + self.set_affine_cpu(cpu_dev) def set_arm_part_number(self, pid): assert pid <= 0xfff, "expected 3 hex digit part number: 0x%x" % pid @@ -552,10 +578,10 @@ def show(self): print(" %20s" % name, end="") print(" %12s" % (d.type_str()), end="") if d.is_affine_to_cpu(): + if d.cpu_number is not None: + print(" %4u" % d.cpu_number, end="") if d.affine_cpu is not None: - print("%5s" % str(d.affine_cpu), end="") - else: - print("%5u" % d.cpu_number, end="") + print(" %4s" % str(d.affine_cpu), end="") else: print(" ", end="") if d.is_hidden: @@ -651,6 +677,7 @@ def test(): Link(df1, ds2, CS_LINK_ATB) p.show() p.check() + print("Self-tests completed.") if __name__ == "__main__": diff --git a/coresight-tools/cs_topology_sysfs.py b/coresight-tools/cs_topology_sysfs.py index 9cd5946..1035673 100644 --- a/coresight-tools/cs_topology_sysfs.py +++ b/coresight-tools/cs_topology_sysfs.py @@ -40,6 +40,8 @@ "ptm": CS_DEVTYPE_TRACE_CORE, "etm": CS_DEVTYPE_TRACE_CORE, "stm": CS_DEVTYPE_TRACE_SW, + "cti_cpu": CS_DEVTYPE_CTI, + "cti_sys": CS_DEVTYPE_CTI, } @@ -113,6 +115,7 @@ def get_cs_from_sysfs(p=None): devtype = devtypes[base] d = Device(p, devtype, name=sd) d.sysfs_path = dp + # See if the device is affine to a (single) CPU try: cn = int(read_file(os.path.join(dp, "cpu"))) d.set_cpu_number(cn) @@ -128,7 +131,10 @@ def get_cs_from_sysfs(p=None): of_node = os.path.join(os.path.dirname(rp), "of_node") if os.path.exists(of_node): d.of_node = os.path.realpath(of_node) # /sys/firmware/devicetree - d.set_mem_address(device_tree_node_address(d.of_node)) + addr = device_tree_node_address(d.of_node) + if addr == 0: + print("warning: %s has zero address" % (d.of_node), file=sys.stderr) + d.set_mem_address(addr) firmware_node = os.path.join(os.path.dirname(rp), "firmware_node") if os.path.exists(firmware_node): firmware_node = os.path.realpath(firmware_node) @@ -172,6 +178,9 @@ def get_cs_from_sysfs(p=None): def device_tree_node_compatibility(dtn): + """ + Get the list of compatibility strings for a device tree node + """ compat = read_file(os.path.join(dtn, "compatible")).split(",") cl = [] for s in compat: @@ -206,11 +215,14 @@ def device_tree_node_reg(dtn, reg_name="reg"): def device_tree_node_property_length(dtn, prop): + """ + For a device tree node, find the #size-cells or #address-cells value, + by looking exactly one level upwards in its directory hierarchy. + (Note that if #address-cells file exists in a node, it indicates the + size of the address field in any childrens' "reg", not its own "reg".) + """ prop = "#" + prop + "-cells" - assert dtn.startswith("/proc/device-tree/"), "unexpected device tree node: %s" % dtn - while len(dtn) > 18 and not os.path.isfile(os.path.join(dtn, prop)): - dtn = os.path.dirname(dtn) - assert dtn != "/proc/device-tree" + dtn = os.path.dirname(dtn) # Go exactly one level up alen = device_tree_node_reg(dtn, reg_name=prop) alen = alen * 4 # it's counted in words return alen @@ -240,7 +252,9 @@ def device_tree_node_size_length(dtn): def device_tree_nodes(): """ - Iterate through nodes in /proc/device-tree + Iterate through nodes in the device tree. + The device tree is exported at /sys/firmware/devicetree/base + and as an alias at /proc/device-tree . """ for root, dirs, files in os.walk("/proc/device-tree"): for d in dirs: @@ -265,6 +279,9 @@ def compat_is_coresight(dcompat): return None +# +# Map device-tree compatibility strings (minus "coresight-") into device types. +# cs_device_tree_types = { "etm3x": CS_DEVTYPE_TRACE_CORE, "ptm": CS_DEVTYPE_TRACE_CORE, @@ -276,6 +293,9 @@ def compat_is_coresight(dcompat): "tpiu": CS_DEVTYPE_PORT, "etb10": CS_DEVTYPE_BUFFER, "tmc": CS_DEVTYPE_BUFFER, + "cti": CS_DEVTYPE_CTI, + "cti-v8-arch": CS_DEVTYPE_CTI, + "cpu-debug": CS_DEVTYPE_CORE, } @@ -399,6 +419,20 @@ def get_cs_from_device_tree(p=None): return p +def list_device_tree_nodes(): + """ + Debugging: just list the device tree nodes. + """ + for (dp, phandle, dcompat) in device_tree_nodes(): + dc = compat_is_coresight(dcompat) + if not dc: + continue + print(" %-20s %4s %-40s" % (dc, phandle, dp), end="") + reg = read_binary_file(os.path.join(dp, "reg")) + print(" reg:%3u" % len(reg), end="") + print(" %s" % str(os.listdir(dp))) + + if __name__ == "__main__": p = get_cs_from_sysfs() if True or p is None or not p.links: