mirror of
https://github.com/AsahiLinux/m1n1
synced 2024-11-22 14:43:08 +00:00
m1n1.hw.dart: Hide all DART variants behind common interface
Allows m1n1 experiments and tracers not to care about the DART variant. Required to trace and experiment with DCP sanely on M1 and M2. Signed-off-by: Janne Grunau <j@jannau.net>
This commit is contained in:
parent
1fc26dbb1a
commit
183991ca19
6 changed files with 450 additions and 465 deletions
|
@ -1,21 +0,0 @@
|
|||
# SPDX-License-Identifier: MIT
|
||||
|
||||
from m1n1.trace.dart8110 import DART8110Tracer
|
||||
|
||||
DART8110Tracer = DART8110Tracer._reloadcls()
|
||||
|
||||
DEVICES = [
|
||||
"/arm-io/dart-apr0",
|
||||
"/arm-io/dart-apr1",
|
||||
]
|
||||
|
||||
dart_tracers = {}
|
||||
|
||||
for i in DEVICES:
|
||||
p.pmgr_adt_clocks_enable(i)
|
||||
tracer = DART8110Tracer(hv, i, verbose=3)
|
||||
tracer.start()
|
||||
|
||||
dart_tracers[i.split("-")[-1]] = tracer
|
||||
|
||||
del tracer
|
|
@ -6,112 +6,35 @@ from enum import IntEnum
|
|||
from ..utils import *
|
||||
from ..malloc import Heap
|
||||
|
||||
__all__ = ["DARTRegs", "DART"]
|
||||
from .dart8020 import DART8020, DART8020Regs
|
||||
from .dart8110 import DART8110, DART8110Regs
|
||||
|
||||
class R_ERROR(Register32):
|
||||
FLAG = 31
|
||||
STREAM = 27, 24
|
||||
CODE = 23, 0
|
||||
NO_DAPF_MATCH = 11
|
||||
WRITE = 10
|
||||
SUBPAGE_PROT = 7
|
||||
PTE_READ_FAULT = 6
|
||||
READ_FAULT = 4
|
||||
WRITE_FAULT = 3
|
||||
NO_PTE = 2
|
||||
NO_PMD = 1
|
||||
NO_TTBR = 0
|
||||
|
||||
class R_STREAM_COMMAND(Register32):
|
||||
INVALIDATE = 20
|
||||
BUSY = 2
|
||||
|
||||
class R_TCR(Register32):
|
||||
BYPASS_DAPF = 12
|
||||
BYPASS_DART = 8
|
||||
TRANSLATE_ENABLE = 7
|
||||
|
||||
class R_TTBR(Register32):
|
||||
VALID = 31
|
||||
ADDR = 30, 0
|
||||
|
||||
class R_REMAP(Register32):
|
||||
MAP3 = 31, 24
|
||||
MAP2 = 23, 16
|
||||
MAP1 = 15, 8
|
||||
MAP0 = 7, 0
|
||||
|
||||
class PTE_T8020(Register64):
|
||||
SP_START = 63, 52
|
||||
SP_END = 51, 40
|
||||
OFFSET = 39, 14
|
||||
SP_PROT_DIS = 1
|
||||
VALID = 0
|
||||
|
||||
class PTE_T6000(Register64):
|
||||
SP_START = 63, 52
|
||||
SP_END = 51, 40
|
||||
OFFSET = 39, 10
|
||||
SP_PROT_DIS = 1
|
||||
VALID = 0
|
||||
|
||||
class R_CONFIG(Register32):
|
||||
LOCK = 15
|
||||
|
||||
class R_DAPF_LOCK(Register32):
|
||||
LOCK = 0
|
||||
|
||||
class DARTRegs(RegMap):
|
||||
STREAM_COMMAND = 0x20, R_STREAM_COMMAND
|
||||
STREAM_SELECT = 0x34, Register32
|
||||
ERROR = 0x40, R_ERROR
|
||||
ERROR_ADDR_LO = 0x50, Register32
|
||||
ERROR_ADDR_HI = 0x54, Register32
|
||||
CONFIG = 0x60, R_CONFIG
|
||||
REMAP = irange(0x80, 4, 4), R_REMAP
|
||||
|
||||
DAPF_LOCK = 0xf0, R_DAPF_LOCK
|
||||
UNK1 = 0xf8, Register32
|
||||
ENABLED_STREAMS = 0xfc, Register32
|
||||
|
||||
TCR = irange(0x100, 16, 4), R_TCR
|
||||
TTBR = (irange(0x200, 16, 16), range(0, 16, 4)), R_TTBR
|
||||
|
||||
PTE_TYPES = {
|
||||
"dart,t8020": PTE_T8020,
|
||||
"dart,t6000": PTE_T6000,
|
||||
}
|
||||
__all__ = ["DART"]
|
||||
|
||||
class DART(Reloadable):
|
||||
PAGE_BITS = 14
|
||||
PAGE_SIZE = 1 << PAGE_BITS
|
||||
|
||||
L0_SIZE = 4 # TTBR count
|
||||
L0_OFF = 36
|
||||
L1_OFF = 25
|
||||
L2_OFF = 14
|
||||
|
||||
IDX_BITS = 11
|
||||
Lx_SIZE = (1 << IDX_BITS)
|
||||
IDX_MASK = Lx_SIZE - 1
|
||||
|
||||
def __init__(self, iface, regs, util=None, compat="dart,t8020", iova_range=(0x80000000, 0x90000000)):
|
||||
self.iface = iface
|
||||
self.regs = regs
|
||||
self.u = util
|
||||
self.pt_cache = {}
|
||||
self.enabled_streams = regs.ENABLED_STREAMS.val
|
||||
self.iova_allocator = [Heap(iova_range[0], iova_range[1], self.PAGE_SIZE)
|
||||
for i in range(16)]
|
||||
self.ptecls = PTE_TYPES[compat]
|
||||
if compat in ["dart,t8020", "dart,t6000"]:
|
||||
self.dart = DART8020(iface, regs, util, compat)
|
||||
elif compat in ["dart,t8110"]:
|
||||
self.dart = DART8110(iface, regs, util)
|
||||
else:
|
||||
raise TypeError(compat)
|
||||
|
||||
@classmethod
|
||||
def from_adt(cls, u, path, instance=0, **kwargs):
|
||||
dart_addr = u.adt[path].get_reg(instance)[0]
|
||||
regs = DARTRegs(u, dart_addr)
|
||||
dart = cls(u.iface, regs, u, **kwargs)
|
||||
dart.ptecls = PTE_TYPES[u.adt[path].compatible[0]]
|
||||
return dart
|
||||
compat = u.adt[path].compatible[0]
|
||||
if compat in ["dart,t8020", "dart,t6000"]:
|
||||
regs = DART8020Regs(u, dart_addr)
|
||||
elif compat in ["dart,t8110"]:
|
||||
regs = DART8110Regs(u, dart_addr)
|
||||
return cls(u.iface, regs, u, compat, **kwargs)
|
||||
|
||||
def ioread(self, stream, base, size):
|
||||
if size == 0:
|
||||
|
@ -151,272 +74,29 @@ class DART(Reloadable):
|
|||
return iova
|
||||
|
||||
def iomap_at(self, stream, iova, addr, size):
|
||||
if size == 0:
|
||||
return
|
||||
|
||||
if not (self.enabled_streams & (1 << stream)):
|
||||
self.enabled_streams |= (1 << stream)
|
||||
self.regs.ENABLED_STREAMS.val |= self.enabled_streams
|
||||
|
||||
tcr = self.regs.TCR[stream].reg
|
||||
|
||||
if tcr.BYPASS_DART and not tcr.TRANSLATE_ENABLE:
|
||||
raise Exception("Stream is bypassed in DART")
|
||||
|
||||
if tcr.BYPASS_DART or not tcr.TRANSLATE_ENABLE:
|
||||
raise Exception(f"Unknown DART mode {tcr}")
|
||||
|
||||
if addr & (self.PAGE_SIZE - 1):
|
||||
raise Exception(f"Unaligned PA {addr:#x}")
|
||||
|
||||
if iova & (self.PAGE_SIZE - 1):
|
||||
raise Exception(f"Unaligned IOVA {iova:#x}")
|
||||
|
||||
start_page = align_down(iova, self.PAGE_SIZE)
|
||||
end = iova + size
|
||||
end_page = align_up(end, self.PAGE_SIZE)
|
||||
|
||||
dirty = set()
|
||||
|
||||
for page in range(start_page, end_page, self.PAGE_SIZE):
|
||||
paddr = addr + page - start_page
|
||||
|
||||
l0 = page >> self.L0_OFF
|
||||
assert l0 < self.L0_SIZE
|
||||
ttbr = self.regs.TTBR[stream, l0].reg
|
||||
if not ttbr.VALID:
|
||||
l1addr = self.u.memalign(self.PAGE_SIZE, self.PAGE_SIZE)
|
||||
self.pt_cache[l1addr] = [0] * self.Lx_SIZE
|
||||
ttbr.VALID = 1
|
||||
ttbr.ADDR = l1addr >> 12
|
||||
self.regs.TTBR[stream, l0].reg = ttbr
|
||||
|
||||
cached, l1 = self.get_pt(ttbr.ADDR << 12)
|
||||
l1idx = (page >> self.L1_OFF) & self.IDX_MASK
|
||||
l1pte = self.ptecls(l1[l1idx])
|
||||
if not l1pte.VALID:
|
||||
l2addr = self.u.memalign(self.PAGE_SIZE, self.PAGE_SIZE)
|
||||
self.pt_cache[l2addr] = [0] * self.Lx_SIZE
|
||||
l1pte = self.ptecls(
|
||||
OFFSET=l2addr >> self.PAGE_BITS, VALID=1, SP_PROT_DIS=1)
|
||||
l1[l1idx] = l1pte.value
|
||||
dirty.add(ttbr.ADDR << 12)
|
||||
else:
|
||||
l2addr = l1pte.OFFSET << self.PAGE_BITS
|
||||
|
||||
dirty.add(l1pte.OFFSET << self.PAGE_BITS)
|
||||
cached, l2 = self.get_pt(l2addr)
|
||||
l2idx = (page >> self.L2_OFF) & self.IDX_MASK
|
||||
self.pt_cache[l2addr][l2idx] = self.ptecls(
|
||||
SP_START=0, SP_END=0xfff,
|
||||
OFFSET=paddr >> self.PAGE_BITS, VALID=1, SP_PROT_DIS=1).value
|
||||
|
||||
for page in dirty:
|
||||
self.flush_pt(page)
|
||||
self.dart.iomap_at(stream, iova, addr, size)
|
||||
|
||||
def iotranslate(self, stream, start, size):
|
||||
if size == 0:
|
||||
return []
|
||||
|
||||
tcr = self.regs.TCR[stream].reg
|
||||
|
||||
if tcr.BYPASS_DART and not tcr.TRANSLATE_ENABLE:
|
||||
return [(start, size)]
|
||||
|
||||
if tcr.BYPASS_DART or not tcr.TRANSLATE_ENABLE:
|
||||
raise Exception(f"Unknown DART mode {tcr}")
|
||||
|
||||
start = start & 0xffffffff
|
||||
|
||||
start_page = align_down(start, self.PAGE_SIZE)
|
||||
start_off = start - start_page
|
||||
end = start + size
|
||||
end_page = align_up(end, self.PAGE_SIZE)
|
||||
end_size = end - (end_page - self.PAGE_SIZE)
|
||||
|
||||
pages = []
|
||||
|
||||
for page in range(start_page, end_page, self.PAGE_SIZE):
|
||||
l0 = page >> self.L0_OFF
|
||||
assert l0 < self.L0_SIZE
|
||||
ttbr = self.regs.TTBR[stream, l0].reg
|
||||
if not ttbr.VALID:
|
||||
pages.append(None)
|
||||
continue
|
||||
|
||||
cached, l1 = self.get_pt(ttbr.ADDR << 12)
|
||||
l1pte = self.ptecls(l1[(page >> self.L1_OFF) & self.IDX_MASK])
|
||||
if not l1pte.VALID and cached:
|
||||
cached, l1 = self.get_pt(ttbr.ADDR << 12, uncached=True)
|
||||
l1pte = self.ptecls(l1[(page >> self.L1_OFF) & self.IDX_MASK])
|
||||
if not l1pte.VALID:
|
||||
pages.append(None)
|
||||
continue
|
||||
|
||||
cached, l2 = self.get_pt(l1pte.OFFSET << self.PAGE_BITS)
|
||||
l2pte = self.ptecls(l2[(page >> self.L2_OFF) & self.IDX_MASK])
|
||||
if not l2pte.VALID and cached:
|
||||
cached, l2 = self.get_pt(l1pte.OFFSET << self.PAGE_BITS, uncached=True)
|
||||
l2pte = self.ptecls(l2[(page >> self.L2_OFF) & self.IDX_MASK])
|
||||
if not l2pte.VALID:
|
||||
pages.append(None)
|
||||
continue
|
||||
|
||||
pages.append(l2pte.OFFSET << self.PAGE_BITS)
|
||||
|
||||
ranges = []
|
||||
|
||||
for page in pages:
|
||||
if not ranges:
|
||||
ranges.append((page, self.PAGE_SIZE))
|
||||
continue
|
||||
laddr, lsize = ranges[-1]
|
||||
if ((page is None and laddr is None) or
|
||||
(page is not None and laddr == (page - lsize))):
|
||||
ranges[-1] = laddr, lsize + self.PAGE_SIZE
|
||||
else:
|
||||
ranges.append((page, self.PAGE_SIZE))
|
||||
|
||||
ranges[-1] = (ranges[-1][0], ranges[-1][1] - self.PAGE_SIZE + end_size)
|
||||
|
||||
if start_off:
|
||||
ranges[0] = (ranges[0][0] + start_off if ranges[0][0] else None,
|
||||
ranges[0][1] - start_off)
|
||||
|
||||
return ranges
|
||||
|
||||
def get_pt(self, addr, uncached=False):
|
||||
cached = True
|
||||
if addr not in self.pt_cache or uncached:
|
||||
cached = False
|
||||
self.pt_cache[addr] = list(
|
||||
struct.unpack(f"<{self.Lx_SIZE}Q", self.iface.readmem(addr, self.PAGE_SIZE)))
|
||||
|
||||
return cached, self.pt_cache[addr]
|
||||
|
||||
def flush_pt(self, addr):
|
||||
assert addr in self.pt_cache
|
||||
self.iface.writemem(addr, struct.pack(f"<{self.Lx_SIZE}Q", *self.pt_cache[addr]))
|
||||
return self.dart.iotranslate(stream, start, size)
|
||||
|
||||
def initialize(self):
|
||||
for i in range(15):
|
||||
self.regs.TCR[i].reg = R_TCR(TRANSLATE_ENABLE=1)
|
||||
self.regs.TCR[15].reg = R_TCR(BYPASS_DART=1)
|
||||
|
||||
for i in range(16):
|
||||
for j in range(4):
|
||||
self.regs.TTBR[i, j].reg = R_TTBR(VALID = 0)
|
||||
|
||||
self.regs.ERROR.val = 0xffffffff
|
||||
self.regs.UNK1.val = 0
|
||||
self.regs.ENABLED_STREAMS.val = 0
|
||||
self.enabled_streams = 0
|
||||
|
||||
self.invalidate_streams()
|
||||
self.dart.initialize()
|
||||
|
||||
def show_error(self):
|
||||
if self.regs.ERROR.reg.FLAG:
|
||||
print(f"ERROR: {self.regs.ERROR.reg!s}")
|
||||
print(f"ADDR: {self.regs.ERROR_ADDR_HI.val:#x}:{self.regs.ERROR_ADDR_LO.val:#x}")
|
||||
self.regs.ERROR.val = 0xffffffff
|
||||
self.dart.show_error()
|
||||
|
||||
def invalidate_streams(self, streams=0xffffffff):
|
||||
self.regs.STREAM_SELECT.val = streams
|
||||
self.regs.STREAM_COMMAND.val = R_STREAM_COMMAND(INVALIDATE=1)
|
||||
while self.regs.STREAM_COMMAND.reg.BUSY:
|
||||
pass
|
||||
self.dart.invalidate_streams(streams)
|
||||
|
||||
def invalidate_cache(self):
|
||||
self.pt_cache = {}
|
||||
|
||||
def dump_table2(self, base, l1_addr):
|
||||
|
||||
def print_block(base, pte, start, last):
|
||||
pgcount = last - start
|
||||
pte.OFFSET -= pgcount
|
||||
print(" page (%4d): %08x ... %08x -> %016x [%d%d]" % (
|
||||
start, base + start*0x4000, base + (start+1)*0x4000,
|
||||
pte.OFFSET << self.PAGE_BITS, pte.SP_PROT_DIS, pte.VALID))
|
||||
if start < last:
|
||||
print(" ==> (%4d): ... %08x -> %016x size: %08x" % (
|
||||
last, base + (last+1)*0x4000,
|
||||
(pte.OFFSET + pgcount - 1) << self.PAGE_BITS, pgcount << self.PAGE_BITS))
|
||||
|
||||
cached, tbl = self.get_pt(l1_addr)
|
||||
|
||||
unmapped = False
|
||||
start = 0
|
||||
next_pte = self.ptecls(VALID=0)
|
||||
|
||||
for i, pte in enumerate(tbl):
|
||||
pte = self.ptecls(pte)
|
||||
if not pte.VALID:
|
||||
if not unmapped:
|
||||
if next_pte.VALID:
|
||||
print_block(base, next_pte, start, i)
|
||||
print(" ...")
|
||||
unmapped = True
|
||||
next_pte = pte
|
||||
continue
|
||||
|
||||
unmapped = False
|
||||
|
||||
if int(pte) != int(next_pte):
|
||||
if next_pte.VALID:
|
||||
print_block(base, next_pte, start, i)
|
||||
start = i
|
||||
|
||||
next_pte = pte
|
||||
next_pte.OFFSET += 1
|
||||
|
||||
if next_pte.VALID:
|
||||
print_block(base, next_pte, start, 2048)
|
||||
|
||||
def dump_table(self, base, l1_addr):
|
||||
cached, tbl = self.get_pt(l1_addr)
|
||||
|
||||
unmapped = False
|
||||
for i, pte in enumerate(tbl):
|
||||
pte = self.ptecls(pte)
|
||||
if not pte.VALID:
|
||||
if not unmapped:
|
||||
print(" ...")
|
||||
unmapped = True
|
||||
continue
|
||||
|
||||
unmapped = False
|
||||
|
||||
print(" table (%d): %08x ... %08x -> %016x [%d%d]" % (
|
||||
i, base + i*0x2000000, base + (i+1)*0x2000000,
|
||||
pte.OFFSET << self.PAGE_BITS, pte.SP_PROT_DIS, pte.VALID))
|
||||
self.dump_table2(base + i*0x2000000, pte.OFFSET << self.PAGE_BITS)
|
||||
|
||||
def dump_ttbr(self, idx, ttbr):
|
||||
if not ttbr.VALID:
|
||||
return
|
||||
|
||||
l1_addr = (ttbr.ADDR) << 12
|
||||
print(" TTBR%d: %09x" % (idx, l1_addr))
|
||||
|
||||
self.dump_table(0, l1_addr)
|
||||
self.dart.invalidate_cache()
|
||||
|
||||
def dump_device(self, idx):
|
||||
tcr = self.regs.TCR[idx].reg
|
||||
ttbrs = self.regs.TTBR[idx, :]
|
||||
print(f"dev {idx:02x}: TCR={tcr!s} TTBRs = [{', '.join(map(str, ttbrs))}]")
|
||||
|
||||
if tcr.TRANSLATE_ENABLE and tcr.BYPASS_DART:
|
||||
print(" mode: INVALID")
|
||||
elif tcr.TRANSLATE_ENABLE:
|
||||
print(" mode: TRANSLATE")
|
||||
|
||||
for idx, ttbr in enumerate(ttbrs):
|
||||
self.dump_ttbr(idx, ttbr.reg)
|
||||
elif tcr.BYPASS_DART:
|
||||
print(" mode: BYPASS")
|
||||
else:
|
||||
print(" mode: UNKNOWN")
|
||||
self.dart.dump_device(idx)
|
||||
|
||||
def dump_all(self):
|
||||
for i in range(16):
|
||||
self.dump_device(i)
|
||||
|
||||
def dump_params(self):
|
||||
self.dart.dump_params()
|
||||
|
|
381
proxyclient/m1n1/hw/dart8020.py
Normal file
381
proxyclient/m1n1/hw/dart8020.py
Normal file
|
@ -0,0 +1,381 @@
|
|||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import struct
|
||||
|
||||
from enum import IntEnum
|
||||
from ..utils import *
|
||||
from ..malloc import Heap
|
||||
|
||||
__all__ = ["DART8020Regs", "DART8020"]
|
||||
|
||||
class R_ERROR(Register32):
|
||||
FLAG = 31
|
||||
STREAM = 27, 24
|
||||
CODE = 23, 0
|
||||
NO_DAPF_MATCH = 11
|
||||
WRITE = 10
|
||||
SUBPAGE_PROT = 7
|
||||
PTE_READ_FAULT = 6
|
||||
READ_FAULT = 4
|
||||
WRITE_FAULT = 3
|
||||
NO_PTE = 2
|
||||
NO_PMD = 1
|
||||
NO_TTBR = 0
|
||||
|
||||
class R_STREAM_COMMAND(Register32):
|
||||
INVALIDATE = 20
|
||||
BUSY = 2
|
||||
|
||||
class R_TCR(Register32):
|
||||
BYPASS_DAPF = 12
|
||||
BYPASS_DART = 8
|
||||
TRANSLATE_ENABLE = 7
|
||||
|
||||
class R_TTBR(Register32):
|
||||
VALID = 31
|
||||
ADDR = 30, 0
|
||||
|
||||
class R_REMAP(Register32):
|
||||
MAP3 = 31, 24
|
||||
MAP2 = 23, 16
|
||||
MAP1 = 15, 8
|
||||
MAP0 = 7, 0
|
||||
|
||||
class PTE_T8020(Register64):
|
||||
SP_START = 63, 52
|
||||
SP_END = 51, 40
|
||||
OFFSET = 39, 14
|
||||
SP_PROT_DIS = 1
|
||||
VALID = 0
|
||||
|
||||
class PTE_T6000(Register64):
|
||||
SP_START = 63, 52
|
||||
SP_END = 51, 40
|
||||
OFFSET = 39, 10
|
||||
SP_PROT_DIS = 1
|
||||
VALID = 0
|
||||
|
||||
class R_CONFIG(Register32):
|
||||
LOCK = 15
|
||||
|
||||
class R_DAPF_LOCK(Register32):
|
||||
LOCK = 0
|
||||
|
||||
class DART8020Regs(RegMap):
|
||||
STREAM_COMMAND = 0x20, R_STREAM_COMMAND
|
||||
STREAM_SELECT = 0x34, Register32
|
||||
ERROR = 0x40, R_ERROR
|
||||
ERROR_ADDR_LO = 0x50, Register32
|
||||
ERROR_ADDR_HI = 0x54, Register32
|
||||
CONFIG = 0x60, R_CONFIG
|
||||
REMAP = irange(0x80, 4, 4), R_REMAP
|
||||
|
||||
DAPF_LOCK = 0xf0, R_DAPF_LOCK
|
||||
UNK1 = 0xf8, Register32
|
||||
ENABLED_STREAMS = 0xfc, Register32
|
||||
|
||||
TCR = irange(0x100, 16, 4), R_TCR
|
||||
TTBR = (irange(0x200, 16, 16), range(0, 16, 4)), R_TTBR
|
||||
|
||||
PTE_TYPES = {
|
||||
"dart,t8020": PTE_T8020,
|
||||
"dart,t6000": PTE_T6000,
|
||||
}
|
||||
|
||||
class DART8020(Reloadable):
|
||||
PAGE_BITS = 14
|
||||
PAGE_SIZE = 1 << PAGE_BITS
|
||||
|
||||
L0_SIZE = 4 # TTBR count
|
||||
L0_OFF = 36
|
||||
L1_OFF = 25
|
||||
L2_OFF = 14
|
||||
|
||||
IDX_BITS = 11
|
||||
Lx_SIZE = (1 << IDX_BITS)
|
||||
IDX_MASK = Lx_SIZE - 1
|
||||
|
||||
def __init__(self, iface, regs, util=None, compat="dart,t8020"):
|
||||
self.iface = iface
|
||||
self.regs = regs
|
||||
self.u = util
|
||||
self.pt_cache = {}
|
||||
self.enabled_streams = regs.ENABLED_STREAMS.val
|
||||
self.ptecls = PTE_TYPES[compat]
|
||||
|
||||
@classmethod
|
||||
def from_adt(cls, u, path, instance=0, **kwargs):
|
||||
dart_addr = u.adt[path].get_reg(instance)[0]
|
||||
dart = cls(u.iface, dart_addr, u)
|
||||
dart.ptecls = PTE_TYPES[u.adt[path].compatible[0]]
|
||||
return dart
|
||||
|
||||
def iomap_at(self, stream, iova, addr, size):
|
||||
if size == 0:
|
||||
return
|
||||
|
||||
if not (self.enabled_streams & (1 << stream)):
|
||||
self.enabled_streams |= (1 << stream)
|
||||
self.regs.ENABLED_STREAMS.val |= self.enabled_streams
|
||||
|
||||
tcr = self.regs.TCR[stream].reg
|
||||
|
||||
if tcr.BYPASS_DART and not tcr.TRANSLATE_ENABLE:
|
||||
raise Exception("Stream is bypassed in DART")
|
||||
|
||||
if tcr.BYPASS_DART or not tcr.TRANSLATE_ENABLE:
|
||||
raise Exception(f"Unknown DART mode {tcr}")
|
||||
|
||||
if addr & (self.PAGE_SIZE - 1):
|
||||
raise Exception(f"Unaligned PA {addr:#x}")
|
||||
|
||||
if iova & (self.PAGE_SIZE - 1):
|
||||
raise Exception(f"Unaligned IOVA {iova:#x}")
|
||||
|
||||
start_page = align_down(iova, self.PAGE_SIZE)
|
||||
end = iova + size
|
||||
end_page = align_up(end, self.PAGE_SIZE)
|
||||
|
||||
dirty = set()
|
||||
|
||||
for page in range(start_page, end_page, self.PAGE_SIZE):
|
||||
paddr = addr + page - start_page
|
||||
|
||||
l0 = page >> self.L0_OFF
|
||||
assert l0 < self.L0_SIZE
|
||||
ttbr = self.regs.TTBR[stream, l0].reg
|
||||
if not ttbr.VALID:
|
||||
l1addr = self.u.memalign(self.PAGE_SIZE, self.PAGE_SIZE)
|
||||
self.pt_cache[l1addr] = [0] * self.Lx_SIZE
|
||||
ttbr.VALID = 1
|
||||
ttbr.ADDR = l1addr >> 12
|
||||
self.regs.TTBR[stream, l0].reg = ttbr
|
||||
|
||||
cached, l1 = self.get_pt(ttbr.ADDR << 12)
|
||||
l1idx = (page >> self.L1_OFF) & self.IDX_MASK
|
||||
l1pte = self.ptecls(l1[l1idx])
|
||||
if not l1pte.VALID:
|
||||
l2addr = self.u.memalign(self.PAGE_SIZE, self.PAGE_SIZE)
|
||||
self.pt_cache[l2addr] = [0] * self.Lx_SIZE
|
||||
l1pte = self.ptecls(
|
||||
OFFSET=l2addr >> self.PAGE_BITS, VALID=1, SP_PROT_DIS=1)
|
||||
l1[l1idx] = l1pte.value
|
||||
dirty.add(ttbr.ADDR << 12)
|
||||
else:
|
||||
l2addr = l1pte.OFFSET << self.PAGE_BITS
|
||||
|
||||
dirty.add(l1pte.OFFSET << self.PAGE_BITS)
|
||||
cached, l2 = self.get_pt(l2addr)
|
||||
l2idx = (page >> self.L2_OFF) & self.IDX_MASK
|
||||
self.pt_cache[l2addr][l2idx] = self.ptecls(
|
||||
SP_START=0, SP_END=0xfff,
|
||||
OFFSET=paddr >> self.PAGE_BITS, VALID=1, SP_PROT_DIS=1).value
|
||||
|
||||
for page in dirty:
|
||||
self.flush_pt(page)
|
||||
|
||||
def iotranslate(self, stream, start, size):
|
||||
if size == 0:
|
||||
return []
|
||||
|
||||
tcr = self.regs.TCR[stream].reg
|
||||
|
||||
if tcr.BYPASS_DART and not tcr.TRANSLATE_ENABLE:
|
||||
return [(start, size)]
|
||||
|
||||
if tcr.BYPASS_DART or not tcr.TRANSLATE_ENABLE:
|
||||
raise Exception(f"Unknown DART mode {tcr}")
|
||||
|
||||
start = start & 0xffffffff
|
||||
|
||||
start_page = align_down(start, self.PAGE_SIZE)
|
||||
start_off = start - start_page
|
||||
end = start + size
|
||||
end_page = align_up(end, self.PAGE_SIZE)
|
||||
end_size = end - (end_page - self.PAGE_SIZE)
|
||||
|
||||
pages = []
|
||||
|
||||
for page in range(start_page, end_page, self.PAGE_SIZE):
|
||||
l0 = page >> self.L0_OFF
|
||||
assert l0 < self.L0_SIZE
|
||||
ttbr = self.regs.TTBR[stream, l0].reg
|
||||
if not ttbr.VALID:
|
||||
pages.append(None)
|
||||
continue
|
||||
|
||||
cached, l1 = self.get_pt(ttbr.ADDR << 12)
|
||||
l1pte = self.ptecls(l1[(page >> self.L1_OFF) & self.IDX_MASK])
|
||||
if not l1pte.VALID and cached:
|
||||
cached, l1 = self.get_pt(ttbr.ADDR << 12, uncached=True)
|
||||
l1pte = self.ptecls(l1[(page >> self.L1_OFF) & self.IDX_MASK])
|
||||
if not l1pte.VALID:
|
||||
pages.append(None)
|
||||
continue
|
||||
|
||||
cached, l2 = self.get_pt(l1pte.OFFSET << self.PAGE_BITS)
|
||||
l2pte = self.ptecls(l2[(page >> self.L2_OFF) & self.IDX_MASK])
|
||||
if not l2pte.VALID and cached:
|
||||
cached, l2 = self.get_pt(l1pte.OFFSET << self.PAGE_BITS, uncached=True)
|
||||
l2pte = self.ptecls(l2[(page >> self.L2_OFF) & self.IDX_MASK])
|
||||
if not l2pte.VALID:
|
||||
pages.append(None)
|
||||
continue
|
||||
|
||||
pages.append(l2pte.OFFSET << self.PAGE_BITS)
|
||||
|
||||
ranges = []
|
||||
|
||||
for page in pages:
|
||||
if not ranges:
|
||||
ranges.append((page, self.PAGE_SIZE))
|
||||
continue
|
||||
laddr, lsize = ranges[-1]
|
||||
if ((page is None and laddr is None) or
|
||||
(page is not None and laddr == (page - lsize))):
|
||||
ranges[-1] = laddr, lsize + self.PAGE_SIZE
|
||||
else:
|
||||
ranges.append((page, self.PAGE_SIZE))
|
||||
|
||||
ranges[-1] = (ranges[-1][0], ranges[-1][1] - self.PAGE_SIZE + end_size)
|
||||
|
||||
if start_off:
|
||||
ranges[0] = (ranges[0][0] + start_off if ranges[0][0] else None,
|
||||
ranges[0][1] - start_off)
|
||||
|
||||
return ranges
|
||||
|
||||
def get_pt(self, addr, uncached=False):
|
||||
cached = True
|
||||
if addr not in self.pt_cache or uncached:
|
||||
cached = False
|
||||
self.pt_cache[addr] = list(
|
||||
struct.unpack(f"<{self.Lx_SIZE}Q", self.iface.readmem(addr, self.PAGE_SIZE)))
|
||||
|
||||
return cached, self.pt_cache[addr]
|
||||
|
||||
def flush_pt(self, addr):
|
||||
assert addr in self.pt_cache
|
||||
self.iface.writemem(addr, struct.pack(f"<{self.Lx_SIZE}Q", *self.pt_cache[addr]))
|
||||
|
||||
def initialize(self):
|
||||
for i in range(15):
|
||||
self.regs.TCR[i].reg = R_TCR(TRANSLATE_ENABLE=1)
|
||||
self.regs.TCR[15].reg = R_TCR(BYPASS_DART=1)
|
||||
|
||||
for i in range(16):
|
||||
for j in range(4):
|
||||
self.regs.TTBR[i, j].reg = R_TTBR(VALID = 0)
|
||||
|
||||
self.regs.ERROR.val = 0xffffffff
|
||||
self.regs.UNK1.val = 0
|
||||
self.regs.ENABLED_STREAMS.val = 0
|
||||
self.enabled_streams = 0
|
||||
|
||||
self.invalidate_streams()
|
||||
|
||||
def show_error(self):
|
||||
if self.regs.ERROR.reg.FLAG:
|
||||
print(f"ERROR: {self.regs.ERROR.reg!s}")
|
||||
print(f"ADDR: {self.regs.ERROR_ADDR_HI.val:#x}:{self.regs.ERROR_ADDR_LO.val:#x}")
|
||||
self.regs.ERROR.val = 0xffffffff
|
||||
|
||||
def invalidate_streams(self, streams=0xffffffff):
|
||||
self.regs.STREAM_SELECT.val = streams
|
||||
self.regs.STREAM_COMMAND.val = R_STREAM_COMMAND(INVALIDATE=1)
|
||||
while self.regs.STREAM_COMMAND.reg.BUSY:
|
||||
pass
|
||||
|
||||
def invalidate_cache(self):
|
||||
self.pt_cache = {}
|
||||
|
||||
def dump_table2(self, base, l1_addr):
|
||||
|
||||
def print_block(base, pte, start, last):
|
||||
pgcount = last - start
|
||||
pte.OFFSET -= pgcount
|
||||
print(" page (%4d): %08x ... %08x -> %016x [%d%d]" % (
|
||||
start, base + start*0x4000, base + (start+1)*0x4000,
|
||||
pte.OFFSET << self.PAGE_BITS, pte.SP_PROT_DIS, pte.VALID))
|
||||
if start < last:
|
||||
print(" ==> (%4d): ... %08x -> %016x size: %08x" % (
|
||||
last, base + (last+1)*0x4000,
|
||||
(pte.OFFSET + pgcount - 1) << self.PAGE_BITS, pgcount << self.PAGE_BITS))
|
||||
|
||||
cached, tbl = self.get_pt(l1_addr)
|
||||
|
||||
unmapped = False
|
||||
start = 0
|
||||
next_pte = self.ptecls(VALID=0)
|
||||
|
||||
for i, pte in enumerate(tbl):
|
||||
pte = self.ptecls(pte)
|
||||
if not pte.VALID:
|
||||
if not unmapped:
|
||||
if next_pte.VALID:
|
||||
print_block(base, next_pte, start, i)
|
||||
print(" ...")
|
||||
unmapped = True
|
||||
next_pte = pte
|
||||
continue
|
||||
|
||||
unmapped = False
|
||||
|
||||
if int(pte) != int(next_pte):
|
||||
if next_pte.VALID:
|
||||
print_block(base, next_pte, start, i)
|
||||
start = i
|
||||
|
||||
next_pte = pte
|
||||
next_pte.OFFSET += 1
|
||||
|
||||
if next_pte.VALID:
|
||||
print_block(base, next_pte, start, 2048)
|
||||
|
||||
def dump_table(self, base, l1_addr):
|
||||
cached, tbl = self.get_pt(l1_addr)
|
||||
|
||||
unmapped = False
|
||||
for i, pte in enumerate(tbl):
|
||||
pte = self.ptecls(pte)
|
||||
if not pte.VALID:
|
||||
if not unmapped:
|
||||
print(" ...")
|
||||
unmapped = True
|
||||
continue
|
||||
|
||||
unmapped = False
|
||||
|
||||
print(" table (%d): %08x ... %08x -> %016x [%d%d]" % (
|
||||
i, base + i*0x2000000, base + (i+1)*0x2000000,
|
||||
pte.OFFSET << self.PAGE_BITS, pte.SP_PROT_DIS, pte.VALID))
|
||||
self.dump_table2(base + i*0x2000000, pte.OFFSET << self.PAGE_BITS)
|
||||
|
||||
def dump_ttbr(self, idx, ttbr):
|
||||
if not ttbr.VALID:
|
||||
return
|
||||
|
||||
l1_addr = (ttbr.ADDR) << 12
|
||||
print(" TTBR%d: %09x" % (idx, l1_addr))
|
||||
|
||||
self.dump_table(0, l1_addr)
|
||||
|
||||
def dump_device(self, idx):
|
||||
tcr = self.regs.TCR[idx].reg
|
||||
ttbrs = self.regs.TTBR[idx, :]
|
||||
print(f"dev {idx:02x}: TCR={tcr!s} TTBRs = [{', '.join(map(str, ttbrs))}]")
|
||||
|
||||
if tcr.TRANSLATE_ENABLE and tcr.BYPASS_DART:
|
||||
print(" mode: INVALID")
|
||||
elif tcr.TRANSLATE_ENABLE:
|
||||
print(" mode: TRANSLATE")
|
||||
|
||||
for idx, ttbr in enumerate(ttbrs):
|
||||
self.dump_ttbr(idx, ttbr.reg)
|
||||
elif tcr.BYPASS_DART:
|
||||
print(" mode: BYPASS")
|
||||
else:
|
||||
print(" mode: UNKNOWN")
|
||||
|
||||
def dump_params(self):
|
||||
pass
|
|
@ -155,7 +155,7 @@ class DART8110Regs(RegMap):
|
|||
|
||||
# Write bits to _PROTECT to protect them.
|
||||
# They can be unprotected by writing to _UNPROTECT unless _LOCK is written.
|
||||
# If _LOCK is written, protection can be enabled but not disabled.
|
||||
# If _LOCK is written, protection can be enabled but not disabled.
|
||||
REG_PROTECT = 0x200, R_PROTECT
|
||||
REG_UNPROTECT = 0x204, R_PROTECT
|
||||
REG_PROTECT_LOCK = 0x208, R_PROTECT
|
||||
|
@ -251,7 +251,7 @@ class DART8110(Reloadable):
|
|||
Lx_SIZE = (1 << IDX_BITS)
|
||||
IDX_MASK = Lx_SIZE - 1
|
||||
|
||||
def __init__(self, iface, regs, util=None, iova_range=(0x80000000, 0x90000000)):
|
||||
def __init__(self, iface, regs, util=None):
|
||||
self.iface = iface
|
||||
self.regs = regs
|
||||
self.u = util
|
||||
|
@ -261,8 +261,6 @@ class DART8110(Reloadable):
|
|||
for i in range(8):
|
||||
enabled_streams |= regs.ENABLE_STREAMS[i].val << 32*i
|
||||
self.enabled_streams = enabled_streams
|
||||
self.iova_allocator = [Heap(iova_range[0], iova_range[1], self.PAGE_SIZE)
|
||||
for i in range(16)]
|
||||
|
||||
@classmethod
|
||||
def from_adt(cls, u, path, instance=0, **kwargs):
|
||||
|
@ -271,43 +269,6 @@ class DART8110(Reloadable):
|
|||
dart = cls(u.iface, regs, u, **kwargs)
|
||||
return dart
|
||||
|
||||
def ioread(self, stream, base, size):
|
||||
if size == 0:
|
||||
return b""
|
||||
|
||||
ranges = self.iotranslate(stream, base, size)
|
||||
|
||||
iova = base
|
||||
data = []
|
||||
for addr, size in ranges:
|
||||
if addr is None:
|
||||
raise Exception(f"Unmapped page at iova {iova:#x}")
|
||||
data.append(self.iface.readmem(addr, size))
|
||||
iova += size
|
||||
|
||||
return b"".join(data)
|
||||
|
||||
def iowrite(self, stream, base, data):
|
||||
if len(data) == 0:
|
||||
return
|
||||
|
||||
ranges = self.iotranslate(stream, base, len(data))
|
||||
|
||||
iova = base
|
||||
p = 0
|
||||
for addr, size in ranges:
|
||||
if addr is None:
|
||||
raise Exception(f"Unmapped page at iova {iova:#x}")
|
||||
self.iface.writemem(addr, data[p:p + size])
|
||||
p += size
|
||||
iova += size
|
||||
|
||||
def iomap(self, stream, addr, size):
|
||||
iova = self.iova_allocator[stream].malloc(size)
|
||||
|
||||
self.iomap_at(stream, iova, addr, size)
|
||||
return iova
|
||||
|
||||
def iomap_at(self, stream, iova, addr, size):
|
||||
if size == 0:
|
||||
return
|
||||
|
@ -573,10 +534,6 @@ class DART8110(Reloadable):
|
|||
else:
|
||||
print(" mode: UNKNOWN")
|
||||
|
||||
def dump_all(self):
|
||||
for i in range(16):
|
||||
self.dump_device(i)
|
||||
|
||||
def dump_params(self):
|
||||
print(self.regs.PARAMS_0.reg)
|
||||
print(self.regs.PARAMS_4.reg)
|
||||
|
|
|
@ -1,33 +1,54 @@
|
|||
# SPDX-License-Identifier: MIT
|
||||
|
||||
from ..hw.dart import *
|
||||
from ..hw.dart8020 import *
|
||||
from ..hw.dart8110 import *
|
||||
from ..hv import TraceMode
|
||||
from ..utils import *
|
||||
from . import ADTDevTracer
|
||||
|
||||
class DARTTracer(ADTDevTracer):
|
||||
RELOAD_DEPS = [DART]
|
||||
|
||||
DEFAULT_MODE = TraceMode.ASYNC
|
||||
|
||||
REGMAPS = [DARTRegs]
|
||||
NAMES = ["regs"]
|
||||
|
||||
@classmethod
|
||||
def _reloadcls(cls, force=False):
|
||||
global DART
|
||||
DART = DART._reloadcls(force)
|
||||
return super()._reloadcls()
|
||||
global DART8020
|
||||
global DART8020Regs
|
||||
global DART8110
|
||||
global DART8110Regs
|
||||
DART8020 = DART8020._reloadcls(force)
|
||||
DART8020Regs = DART8020Regs._reloadcls(force)
|
||||
DART8110 = DART8110._reloadcls(force)
|
||||
DART8110Regs = DART8110Regs._reloadcls(force)
|
||||
return super()._reloadcls(force)
|
||||
|
||||
def __init__(self, hv, devpath, **kwargs):
|
||||
compat = hv.adt[devpath].compatible[0]
|
||||
if compat in ["dart,t6000", "dart,t8020"]:
|
||||
self.REGMAPS = [DART8020Regs]
|
||||
elif compat in ["dart,t8110"]:
|
||||
self.REGMAPS = [DART8110Regs]
|
||||
|
||||
return super().__init__(hv, devpath, **kwargs)
|
||||
|
||||
def start(self):
|
||||
super().start()
|
||||
# prime cache
|
||||
for i in range(16):
|
||||
self.regs.TCR[i].val
|
||||
for j in range(4):
|
||||
self.regs.TTBR[i, j].val
|
||||
|
||||
self.regs.ENABLED_STREAMS.val
|
||||
if self.dev.compatible[0] == "dart,t8110":
|
||||
for i in range(16):
|
||||
self.regs.TCR[i].val
|
||||
self.regs.TTBR[i].val
|
||||
for _ in range(8):
|
||||
self.regs.ENABLE_STREAMS[_].val
|
||||
else:
|
||||
for i in range(16):
|
||||
self.regs.TCR[i].val
|
||||
for j in range(4):
|
||||
self.regs.TTBR[i, j].val
|
||||
self.regs.ENABLED_STREAMS.val
|
||||
|
||||
self.dart = DART(self.hv.iface, self.regs, compat=self.dev.compatible[0])
|
||||
|
||||
|
@ -36,3 +57,13 @@ class DARTTracer(ADTDevTracer):
|
|||
if stream_command.INVALIDATE:
|
||||
self.log(f"Invalidate Stream: {self.regs.cached.STREAM_SELECT.reg}")
|
||||
self.dart.invalidate_cache()
|
||||
|
||||
def w_TLB_OP(self, tlb_op):
|
||||
if tlb_op.OP == 0:
|
||||
self.log(f"Invalidate all")
|
||||
self.dart.invalidate_cache()
|
||||
elif tlb_op.OP == 1:
|
||||
self.log(f"Invalidate Stream: {tlb_op.STREAM}")
|
||||
self.dart.invalidate_cache()
|
||||
else:
|
||||
self.log(f"Unknown TLB op {tlb_op}")
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
# SPDX-License-Identifier: MIT
|
||||
|
||||
from ..hw.dart8110 import *
|
||||
from ..hv import TraceMode
|
||||
from ..utils import *
|
||||
from . import ADTDevTracer
|
||||
|
||||
class DART8110Tracer(ADTDevTracer):
|
||||
RELOAD_DEPS = [DART8110]
|
||||
|
||||
DEFAULT_MODE = TraceMode.SYNC
|
||||
|
||||
REGMAPS = [DART8110Regs]
|
||||
NAMES = ["regs"]
|
||||
|
||||
@classmethod
|
||||
def _reloadcls(cls):
|
||||
global DART8110
|
||||
DART8110 = DART8110._reloadcls()
|
||||
return super()._reloadcls()
|
||||
|
||||
def start(self):
|
||||
super().start()
|
||||
# prime cache
|
||||
for i in range(16):
|
||||
self.regs.TCR[i].val
|
||||
self.regs.TTBR[i].val
|
||||
|
||||
for _ in range(8):
|
||||
self.regs.ENABLE_STREAMS[0].val
|
||||
|
||||
self.dart = DART8110(self.hv.iface, self.regs)
|
||||
|
||||
|
||||
def w_TLB_OP(self, tlb_op):
|
||||
if tlb_op.OP == 0:
|
||||
self.log(f"Invalidate all")
|
||||
self.dart.invalidate_cache()
|
||||
elif tlb_op.OP == 1:
|
||||
self.log(f"Invalidate Stream: {tlb_op.STREAM}")
|
||||
self.dart.invalidate_cache()
|
||||
else:
|
||||
self.log(f"Unknown TLB op {tlb_op}")
|
Loading…
Reference in a new issue