diff options
Diffstat (limited to 'tools/proxyclient/experiments/mmio_sweep.py')
| -rwxr-xr-x | tools/proxyclient/experiments/mmio_sweep.py | 212 |
1 files changed, 212 insertions, 0 deletions
diff --git a/tools/proxyclient/experiments/mmio_sweep.py b/tools/proxyclient/experiments/mmio_sweep.py new file mode 100755 index 0000000..938ff29 --- /dev/null +++ b/tools/proxyclient/experiments/mmio_sweep.py @@ -0,0 +1,212 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: MIT +import sys, pathlib +import time +sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) + +from m1n1.setup import * +from m1n1.loadobjs import * +import argparse +import numpy as np + +argparser = argparse.ArgumentParser() +argparser.add_argument("-d", "--domain", type=str, + help='Look for MMIO range associated with a particular' + ' power domain') +argparser.add_argument("-p", "--print", action='store_true', + help='Print power domain list') +args = argparser.parse_args() + +if args.print: + for dev in u.adt["/arm-io/pmgr"].devices: + print(dev.name) + sys.exit(0) + +granule = 0x4000 + +lp = LinkedProgram(u) +lp.load_inline_c(f''' + #define GRANULE {granule} + ''' + ''' + #include "exception.h" + #include "utils.h" + #include "soc.h" + + bool is_t6000(void) + { + return chip_id == T6000; + } + + void sweep(u64 from, u64 to, u64 target) + { + u32 *mask = (u32 *) target; + exc_guard = GUARD_MARK | GUARD_SILENT; + + int bitp = 0; + for (u64 p = from; p < to; p += GRANULE) { + sysop("dsb sy"); + sysop("isb"); + bool hit = read32(p) != 0xabad1dea; + + if (hit) + *mask |= (1 << bitp); + else + *mask &= ~(1 << bitp); + + if (++bitp >= 32) { + bitp = 0; + mask++; + } + } + + sysop("dsb sy"); + sysop("isb"); + } + ''') + +def do_sweep(maskrange): + masklen = (maskrange.stop - maskrange.start) // granule // 32 * 4 + 4 + mask_base = u.heap.malloc(masklen) + lp.sweep(maskrange.start, maskrange.stop, mask_base) + mask = iface.readmem(mask_base, masklen) + u.heap.free(mask_base) + return np.frombuffer(mask, dtype=np.uint8) + +def describe_mask(mask, maskrange): + ''' + Describe mask in terms of hot from-to ranges + ''' + ranges = [] + prev_hit = False + mask = np.concatenate((mask, [0])) + for i in range(len(mask)*8): + hit = mask[i//8] & (1<<(i%8)) != 0 + if hit and not prev_hit: + start = maskrange.start + i*granule + if not hit and prev_hit: + end = maskrange.start + i*granule + ranges.append((start, end)) + prev_hit = hit + return ranges + +if lp.is_t6000(): + maskrange = range(0x2_9000_0000, 0x4_0000_0000) +else: + maskrange = range(0x2_2000_0000, 0x3_0000_0000) + +pd_did_enable = set() +pmgr = u.adt["/arm-io/pmgr"] +ps_dev_by_id = {dev.id: dev for dev in pmgr.devices} +ps_deps = dict() +ps_addrs = dict() + +for dev in pmgr.devices: + ps = pmgr.ps_regs[dev.psreg] + addr = pmgr.get_reg(ps.reg)[0] + ps.offset + dev.psidx * 8 + + if lp.is_t6000() and dev.name.startswith("AOP_"): + addr = 0x292284000 + (dev.id - 403) * 8 + + ps_addrs[dev.name] = addr + ps_deps[dev.name] = [ + ps_dev_by_id[idx].name for idx + in dev.parents if idx in ps_dev_by_id + ] + +if lp.is_t6000(): + # on t6000, guess the AOP PD hierarchy (undocumented + # in ADT) by analogy with t8103 + ps_deps["AOP_GPIO"] += ["AOP_FILTER"] + ps_deps["AOP_BASE"] += ["AOP_FILTER"] + ps_deps["AOP_FR"] += ["AOP_FILTER"] + ps_deps["AOP_SPMI0"] += ["AOP_FR"] + ps_deps["AOP_SPMI1"] += ["AOP_FR"] + ps_deps["AOP_LEAP_CLK"] += ["AOP_FILTER"] + ps_deps["AOP_SHIM"] += ["AOP_BASE"] + ps_deps["AOP_UART0"] += ["AOP_SHIM"] + ps_deps["AOP_UART1"] += ["AOP_SHIM"] + ps_deps["AOP_UART2"] += ["AOP_SHIM"] + ps_deps["AOP_SCM"] += ["AOP_BASE", "AOP_FR"] + ps_deps["AOP_CPU"] += ["AOP_BASE"] + ps_deps["AOP_I2CM0"] += ["AOP_FR"] + ps_deps["AOP_I2CM1"] += ["AOP_FR"] + ps_deps["AOP_MCA0"] += ["AOP_FR", "AOP_SHIM"] + ps_deps["AOP_MCA1"] += ["AOP_FR", "AOP_SHIM"] + ps_deps["AOP_SPI0"] += ["AOP_FR"] + ps_deps["AOP_LEAP"] += ["AOP_LEAP_CLK"] + ps_deps["AOP_AUDIO_SHIM"] += ["AOP_LEAP_CLK"] + ps_deps["AOP_AUDIO_ADMA0"] += ["AOP_FR"] + ps_deps["AOP_PDMC_LPD"] += ["AOP_SHIM"] + ps_deps["AOP_SRAM"] += ["AOP_SCM", "AOP_CPU"] + +def ps_pstate(name): + return p.read32(ps_addrs[name]) & 0x0f + +def ps_enabled(name): + return p.read32(ps_addrs[name]) & 0x0f == 0x0f + +def ps_set_pstate(name, desired): + p.mask32(ps_addrs[name], 0xf, desired) + time.sleep(0.001) + actual = p.read32(ps_addrs[name]) + if actual & 0xf0 != desired << 4: + print("WARNING: %s stuck at pstate 0x%x (desired 0x%x)" \ + % (name, actual >> 4, desired)) + +def ps_enable(name): + print("Enabling %s..." % name) + ps_set_pstate(name, 0xf) + +def ps_disable(name): + p.mask32(ps_addrs[name], 0xf, 0x0) + +if args.domain: + ps_disable(args.domain) + + to_enable = set([args.domain]) + for dev in reversed(pmgr.devices): + if dev.name not in to_enable \ + or ps_enabled(dev.name): + continue + + for dep in ps_deps[dev.name]: + to_enable.add(dep) + + save = dict() + for dev in pmgr.devices: + if dev.name in to_enable: + save[dev.name] = ps_pstate(dev.name) + if dev.name != args.domain: + ps_enable(dev.name) + + premask = do_sweep(maskrange) + ps_enable(args.domain) + postmask = do_sweep(maskrange) + + print("Reverting...") + + for dev in reversed(pmgr.devices): + if dev.name in to_enable and dev.name: + ps_set_pstate(dev.name, save[dev.name]) + + hitmask = premask ^ postmask + if np.count_nonzero(hitmask & premask): + print("Que? Some ranges disappeared?") +else: + # no --domain flag, do a plain sweep + hitmask = do_sweep(maskrange) + +al = u.adt.build_addr_lookup() +for start, stop in describe_mask(hitmask, maskrange): + # bit ugly but it makes addrlookup do all the heavy lifting for us + al.add(range(start, stop), "hit") + +print("Hits:") +for zone, value in al.items(): + if ((zone.start - 1) // granule + 1) * granule >= zone.stop: + continue + if not any([v[0] == "hit" for v in value]): + continue + + labels = set([v[0] for v in value if v[0] != "hit"]) + print(f"\t{zone.start:9x} - {zone.stop:9x} | {' '.join(labels)}") |
