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())
|