aboutsummaryrefslogtreecommitdiffstats
path: root/psp_out/pte_attr_probe.py
blob: 7b00386f45c182b31155acf86c9d475ae9e79cea (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#!/usr/bin/env python3
"""Does any PTE attribute change what a GPU engine reads from the TMR?

TMZ (bit 3) did not. This varies the cacheability/allocation attributes --
MTYPE (NV10 mask, bits 48-50) and NOALLOC (bit 58) -- on the same mapping. The
idea: if the fence were expressed at a cache/coherence level rather than as a
hard physical-address block, an uncached or no-allocate access might behave
differently.

Also maps the fence BOUNDARY from the GPU path, which is independently useful:
the TMR range is documented as 0x300000..0x1600000 from the SMU experiment, and
confirming those edges with a different initiator either corroborates it or
finds the real edges.

Same validated harness as the TMZ run: every configuration is preceded by a
control read of a page we own.
"""
import time
import numpy as np
from tinygrad import Device, Tensor
from tinygrad.runtime.support.memory import AddrSpace, PageTableTraverseContext
import tinygrad.runtime.autogen.am.am as am

OUT = "/tmp/pteattr.txt"


def say(s):
    with open(OUT, "a") as f:
        f.write(s + "\n")
    print(s, flush=True)


d = Device["AMD"]
adev = d.iface.dev_impl
mm = adev.mm
hb = adev.reg("regMP0_SMN_C2PMSG_81")
SZ = 0x1000


def alive():
    a = hb.read(); time.sleep(0.2); return hb.read() != a


src = (Tensor.zeros(SZ, dtype="uint8").contiguous() + 1).contiguous().realize()
va = src.uop.buffer._buf
scratch = mm.palloc(SZ, zero=True, boot=False)
adev.vram[scratch:scratch + SZ] = bytes([0x3C]) * SZ
say("\n######## PTE attribute / boundary probe ########")
say("VA %#x  scratch PA %#x (0x3C fill)" % (va, scratch))


def remap(pa, orbits=0, clearbits=0):
    mm.unmap_range(va, SZ)
    mm.map_range(va, SZ, [(pa, SZ)], aspace=AddrSpace.PHYS)
    if orbits or clearbits:
        ctx = PageTableTraverseContext(adev, mm.root_page_table, va, inspect=True)
        for _o, pt, i, cnt, _c in ctx.next(SZ):
            for k in range(cnt):
                pt.entries[i + k] = (pt.entries[i + k] & ~clearbits) | orbits
    adev.gmc.flush_tlb(ip='GC', vmid=0)


def read(tag):
    try:
        b = (src + 0).realize().numpy().astype(np.uint8)
        u = np.unique(b)
        say("   %-30s %s  unique=%d" % (tag, bytes(b[:12]).hex(), len(u)))
        return b
    except Exception as e:
        say("   %-30s FAILED %s" % (tag, type(e).__name__))
        return None


MT = am.AMDGPU_PTE_MTYPE_NV10_MASK
NOALLOC = am.AMDGPU_PTE_NOALLOC
TMR_PA = 0x900000

remap(scratch)
c = read("control scratch")
if c is None or np.unique(c)[0] != 0x3C or len(np.unique(c)) != 1:
    say("control invalid -- stop"); raise SystemExit(0)
say("   control VALID")

say("\nA) attribute sweep on the TMR page")
for name, orb, clr in (("default", 0, 0),
                       ("MTYPE cleared", 0, MT),
                       ("MTYPE=1<<48", 1 << 48, MT),
                       ("MTYPE=2<<48", 2 << 48, MT),
                       ("MTYPE=3<<48", 3 << 48, MT),
                       ("NOALLOC", NOALLOC, 0),
                       ("NOALLOC+MTYPE cleared", NOALLOC, MT)):
    remap(TMR_PA, orb, clr)
    read(name)
    if not alive():
        say("   heartbeat stopped"); break
say("   psp alive %s" % alive())

say("\nB) fence boundary from the GPU path (documented TMR 0x300000..0x1600000)")
for pa in (0x200000, 0x2FF000, 0x300000, 0x400000, 0x900000,
           0x15FF000, 0x1600000, 0x1700000, 0x2000000):
    remap(pa)
    b = read("PA %#09x" % pa)
say("   psp alive %s" % alive())