diff options
| author | magh <magh@maghmogh.com> | 2023-03-06 18:44:55 -0600 |
|---|---|---|
| committer | magh <magh@maghmogh.com> | 2023-03-06 18:44:55 -0600 |
| commit | e80d9d8871b325a04b18f90a9ea4bb7fd148fb25 (patch) | |
| tree | 79dbdb8506b7ff1e92549188d1b94cfc0b3503ae /tools/proxyclient/m1n1/hw | |
Diffstat (limited to 'tools/proxyclient/m1n1/hw')
25 files changed, 5944 insertions, 0 deletions
diff --git a/tools/proxyclient/m1n1/hw/admac.py b/tools/proxyclient/m1n1/hw/admac.py new file mode 100644 index 0000000..3fb8032 --- /dev/null +++ b/tools/proxyclient/m1n1/hw/admac.py @@ -0,0 +1,416 @@ +# SPDX-License-Identifier: MIT +import sys, time +from enum import IntEnum +from ..utils import * + +__all__ = ["ADMACRegs", "ADMAC", "E_BUSWIDTH", "E_FRAME"] + + +class R_RING(Register32): + # overflow/underflow counter + OF_UF = 31, 16 + + # goes through 0, 1, 2, 3 as the pieces of a report/descriptor + # are being read/written through REPORT_READ/DESC_WRITE + READOUT_PROGRESS = 13, 12 + + # when READ_SLOT==WRITE_SLOT one of the two is set + EMPTY = 8 + FULL = 9 + + ERR = 10 + + # next slot to read + READ_SLOT = 5, 4 + + # next slot to be written to + WRITE_SLOT = 1, 0 + +class R_CHAN_STATUS(Register32): + # only raised if the descriptor had NOTIFY set + DESC_DONE = 0 + + DESC_RING_EMPTY = 4 + REPORT_RING_FULL = 5 + + # cleared by writing ERR=1 either to TX_DESC_RING or TX_REPORT_RING + RING_ERR = 6 + + UNK0 = 1 + UNK3 = 8 + UNK4 = 9 + UNK5 = 10 + +class R_CHAN_CONTROL(Register32): + RESET_RINGS = 0 + CLEAR_OF_UF_COUNTERS = 1 + UNK1 = 3 + +class E_BUSWIDTH(IntEnum): + W_8BIT = 0 + W_16BIT = 1 + W_32BIT = 2 + +class E_FRAME(IntEnum): + F_1_WORD = 0 + F_2_WORDS = 1 + F_4_WORDS = 2 + +class R_BUSWIDTH(Register32): + WORD = 2, 0, E_BUSWIDTH + FRAME = 6, 4, E_FRAME + +class R_CARVEOUT(Register32): + SIZE = 31, 16 + BASE = 15, 0 + +class ADMACRegs(RegMap): + TX_EN = 0x0, Register32 # one bit per channel + TX_EN_CLR = 0x4, Register32 + + RX_EN = 0x8, Register32 + RX_EN_CLR = 0xc, Register32 + + UNK_CTL = 0x10, Register32 + + # each of the four registers represents an internal interrupt line, + # bits represent DMA channels which at the moment raise that particular line + # + # the irq-destination-index prop in ADT maybe selects the line which + # is actually wired out + # + TX_INTSTATE = irange(0x30, 4, 0x4), Register32 + RX_INTSTATE = irange(0x40, 4, 0x4), Register32 + + # a 24 MHz always-running counter, top bit is always set + COUNTER = 0x70, Register64 + + TX_SRAM_SIZE = 0x94, Register32 + RX_SRAM_SIZE = 0x98, Register32 + + # -- per-channel registers -- + + CHAN_CTL = (irange(0x8000, 32, 0x200)), R_CHAN_CONTROL + + CHAN_BUSWIDTH = (irange(0x8040, 32, 0x200)), R_BUSWIDTH + CHAN_SRAM_CARVEOUT = (irange(0x8050, 32, 0x200)), R_CARVEOUT + CHAN_BURSTSIZE = (irange(0x8054, 32, 0x200)), Register32 + + CHAN_RESIDUE = irange(0x8064, 32, 0x200), Register32 + + CHAN_DESC_RING = irange(0x8070, 32, 0x200), R_RING + CHAN_REPORT_RING = irange(0x8074, 32, 0x200), R_RING + + TX_DESC_WRITE = irange(0x10000, 16, 4), Register32 + TX_REPORT_READ = irange(0x10100, 16, 4), Register32 + + RX_DESC_WRITE = irange(0x14000, 16, 4), Register32 + RX_REPORT_READ = irange(0x14100, 16, 4), Register32 + + # per-channel, per-internal-line + CHAN_STATUS = (irange(0x8010, 32, 0x200), irange(0x0, 4, 0x4)), R_CHAN_STATUS + CHAN_INTMASK = (irange(0x8020, 32, 0x200), irange(0x0, 4, 0x4)), R_CHAN_STATUS + + +class ADMACDescriptorFlags(Register32): + # whether to raise DESC_DONE in CHAN_STATUS + NOTIFY = 16 + + # whether to repeat this descriptor ad infinitum + # + # once a descriptor with this flag is loaded, any descriptors loaded + # afterwards are also repeated and nothing short of full power domain reset + # seems to revoke that behaviour. this looks like a HW bug. + REPEAT = 17 + + # arbitrary ID propagated into reports + DESC_ID = 7, 0 + +class ADMACDescriptor(Reloadable): + def __init__(self, addr, length, **flags): + self.addr = addr + self.length = length + self.flags = ADMACDescriptorFlags(**flags) + + def __repr__(self): + return f"<descriptor: addr=0x{self.addr:x} len=0x{self.length:x} flags={self.flags}>" + + def ser(self): + return [ + self.addr & (1<<32)-1, + self.addr>>32 & (1<<32)-1, + self.length & (1<<32)-1, + int(self.flags) + ] + + @classmethod + def deser(self, seq): + if not len(seq) == 4: + raise ValueError + return ADMACDescriptor( + seq[0] | seq[1] << 32, # addr + seq[2], # length (in bytes) + **ADMACDescriptorFlags(seq[3]).fields + ) + + +class ADMACReportFlags(Register32): + UNK1 = 24 + UNK2 = 25 + UNK4 = 26 # memory access fault? + UNK3 = 27 + DESC_ID = 7, 0 + +class ADMACReport(Reloadable): + def __init__(self, countval, unk1, flags): + self.countval, self.unk1, self.flags = countval, unk1, ADMACReportFlags(flags) + + def __repr__(self): + return f"<report: countval=0x{self.countval:x} unk1=0x{self.unk1:x} flags={self.flags}>" + + def ser(self): + return [ + self.countval & (1<<32)-1, + self.countval>>32 & (1<<32)-1, + self.unk1 & (1<<32)-1, + int(self.flags) + ] + + @classmethod + def deser(self, seq): + if not len(seq) == 4: + raise ValueError + return ADMACReport( + seq[0] | seq[1] << 32, # countval + seq[2], # unk1 + seq[3] # flags + ) + + +class ADMACChannel(Reloadable): + def __init__(self, parent, channo): + self.p = parent + self.iface = parent.p.iface + self.dart = parent.dart + self.regs = parent.regs + self.tx = (channo % 2) == 0 + self.rx = not self.tx + self.ch = channo + + self._desc_id = 0 + self._submitted = {} + self._last_report = None + self._est_byte_rate = None + + def reset(self): + self.regs.CHAN_CTL[self.ch].set(RESET_RINGS=1, CLEAR_OF_UF_COUNTERS=1) + self.regs.CHAN_CTL[self.ch].set(RESET_RINGS=0, CLEAR_OF_UF_COUNTERS=0) + + self.burstsize = 0xc0_0060 + self.buswidth = E_BUSWIDTH.W_32BIT + self.framesize = E_FRAME.F_1_WORD + + def enable(self): + self.regs.CHAN_INTMASK[self.ch, 0].reg = \ + R_CHAN_STATUS(DESC_DONE=1, DESC_RING_EMPTY=1, + REPORT_RING_FULL=1, RING_ERR=1) + + if self.tx: + self.regs.TX_EN.val = 1 << (self.ch//2) + else: + self.regs.RX_EN.val = 1 << (self.ch//2) + + def disable(self): + if self.tx: + self.regs.TX_EN_CLR.val = 1 << (self.ch//2) + else: + self.regs.RX_EN_CLR.val = 1 << (self.ch//2) + + @property + def buswidth(self): + self.regs.CHAN_BUSWIDTH[self.ch].reg.WORD + + @buswidth.setter + def buswidth(self, wordsize): + return self.regs.CHAN_BUSWIDTH[self.ch].set(WORD=wordsize) + + @property + def framesize(self): + self.regs.CHAN_BUSWIDTH[self.ch].reg.FRAME + + @framesize.setter + def framesize(self, framesize): + return self.regs.CHAN_BUSWIDTH[self.ch].set(FRAME=framesize) + + @property + def burstsize(self): + return self.regs.CHAN_BURSTSIZE[self.ch].val + + @burstsize.setter + def burstsize(self, size): + self.regs.CHAN_BURSTSIZE[self.ch].val = size + + @property + def sram_carveout(self): + reg = self.regs.CHAN_SRAM_CARVEOUT[self.ch].reg + return (reg.BASE, reg.SIZE) + + @sram_carveout.setter + def sram_carveout(self, carveout): + base, size = carveout + self.regs.CHAN_SRAM_CARVEOUT[self.ch].reg = \ + R_CARVEOUT(BASE=base, SIZE=size) + + @property + def DESC_WRITE(self): + if self.tx: + return self.regs.TX_DESC_WRITE[self.ch//2] + else: + return self.regs.RX_DESC_WRITE[self.ch//2] + + @property + def REPORT_READ(self): + if self.tx: + return self.regs.TX_REPORT_READ[self.ch//2] + else: + return self.regs.RX_REPORT_READ[self.ch//2] + + def can_submit(self): + return not self.regs.CHAN_DESC_RING[self.ch].reg.FULL + + def submit_desc(self, desc): + if self.regs.CHAN_DESC_RING[self.ch].reg.FULL: + raise Exception(f"ch{self.ch} descriptor ring full") + + if self.p.debug: + print(f"admac: submitting (ch{self.ch}): {desc}", file=sys.stderr) + + for piece in desc.ser(): + self.DESC_WRITE.val = piece + + self._submitted[desc.flags.DESC_ID] = desc + + def submit(self, data=None, buflen=None, **kwargs): + if self.tx: + assert data is not None + buflen = len(data) + else: + assert buflen is not None + + iova = self.p.get_buffer(buflen) + if self.tx: + self.p.iowrite(iova, data) + self.submit_desc(ADMACDescriptor( + iova, buflen, DESC_ID=self._desc_id, NOTIFY=1, **kwargs + )) + self._desc_id = (self._desc_id + 1) % 256 + + def read_reports(self): + data = bytearray() + + while not self.regs.CHAN_REPORT_RING[self.ch].reg.EMPTY: + pieces = [] + for _ in range(4): + pieces.append(self.REPORT_READ.val) + report = ADMACReport.deser(pieces) + + if report.flags.DESC_ID in self._submitted: + desc = self._submitted[report.flags.DESC_ID] + else: + print(f"admac: stray report (ch{self.ch}): {report}", file=sys.stderr) + desc = None + + if self.rx and desc and self.p.dart: + data.extend(self.p.ioread(desc.addr, desc.length)) + + if self.p.debug: + if self._last_report and desc: + countval_delta = report.countval - self._last_report.countval + est_rate = 24e6*desc.length/countval_delta/4 + est = f"(estimated rate: {est_rate:.2f} dwords/s)" + else: + est = "" + + print(f"admac: picked up (ch{self.ch}): {report} {est}", file=sys.stderr) + + self._last_report = report + + return data if self.rx else None + + @property + def status(self): + return self.regs.CHAN_STATUS[self.ch, 0].reg + + def poll(self, wait=True): + while not (self.status.DESC_DONE or self.status.RING_ERR): + time.sleep(0.001) + + if not wait: + break + + self.regs.CHAN_STATUS[self.ch,0].reg = R_CHAN_STATUS(DESC_DONE=1) + + if self.status.RING_ERR: + if self.p.debug: + print(f"STATUS={self.regs.CHAN_STATUS[self.ch,1].reg} " + \ + f"REPORT_RING={self.regs.CHAN_DESC_RING[self.ch]} " + \ + f"DESC_RING={self.regs.CHAN_REPORT_RING[self.ch]}", + file=sys.stderr) + self.regs.CHAN_DESC_RING[self.ch].set(ERR=1) + self.regs.CHAN_REPORT_RING[self.ch].set(ERR=1) + + return self.read_reports() + + +class ADMAC(Reloadable): + def __init__(self, u, devpath, dart=None, dart_stream=2, + reserved_size=4*1024*1024, debug=False): + self.u = u + self.p = u.proxy + self.debug = debug + + if type(devpath) is str: + adt_node = u.adt[devpath] + # ADT's #dma-channels counts pairs of RX/TX channel, so multiply by two + self.nchans = adt_node._properties["#dma-channels"] * 2 + self.base, _ = adt_node.get_reg(0) + else: + self.base = devpath + self.nchans = 26 + + self.regs = ADMACRegs(u, self.base) + self.dart, self.dart_stream = dart, dart_stream + + if dart is not None: + resmem_phys = u.heap.memalign(128*1024, reserved_size) + self.resmem_iova = self.dart.iomap(dart_stream, resmem_phys, reserved_size) + self.resmem_size = reserved_size + self.resmem_pos = 0 + self.dart.invalidate_streams(1 << dart_stream) + + self.chans = [ADMACChannel(self, no) for no in range(self.nchans)] + + def ioread(self, base, size): + assert self.dart is not None + return self.dart.ioread(self.dart_stream, base, size) + + def iowrite(self, base, data): + assert self.dart is not None + self.dart.iowrite(self.dart_stream, base, data) + + def fill_canary(self): + ranges = self.dart.iotranslate(self.dart_stream, + self.resmem_iova, self.resmem_size) + assert len(ranges) == 1 + start, size = ranges[0] + self.p.memset8(start, 0xba, size) + + def get_buffer(self, size): + assert size < self.resmem_size + + if self.resmem_pos + size > self.resmem_size: + self.resmem_pos = 0 + + bufptr = self.resmem_iova + self.resmem_pos + self.resmem_pos += size + return bufptr diff --git a/tools/proxyclient/m1n1/hw/aes.py b/tools/proxyclient/m1n1/hw/aes.py new file mode 100644 index 0000000..7e09335 --- /dev/null +++ b/tools/proxyclient/m1n1/hw/aes.py @@ -0,0 +1,110 @@ +# SPDX-License-Identifier: MIT +from ..utils import * +from enum import IntEnum +from .dart import DART, DARTRegs +import struct +from enum import IntEnum + + +class AES_OPCODE(IntEnum): + # 0 triggers an invalid command interrupt + SET_KEY = 1 + SET_IV = 2 + # 0x03 seems to take three additional argument, function unknown + # 0x04 seems to take one additional argument, function unknown + CRYPT = 5 + GET_IV = 6 + # 0x07 takes one additional argument, function unknown + BARRIER = 8 # can be used to trigger an IRQ but possibly also does more + # > 8 trigger an invalid command interrupt + + +class AES_SET_KEY_LEN(IntEnum): + AES128 = 0 + AES192 = 1 + AES256 = 2 + + +class AES_SET_KEY_BLOCK_MODE(IntEnum): + ECB = 0 + CBC = 1 + CTR = 2 + + +class AESCommandBase(Register32): + OPCODE = 31, 28, AES_OPCODE + + +class AESHwKey(IntEnum): + SOFTWARE = 0 + UID = 1 # unique key for each chip + GID0 = 2 # (probably) globally unique key within a chip family + GID1 = 3 # globally unique key within a chip family + # 4-7 are probably empty / reserved for future use + + +class AESSetKeyCommand(AESCommandBase): + OPCODE = 31, 28, Constant(AES_OPCODE.SET_KEY) + SLOT = 27, 27 + KEY_SELECT = 26, 24 + KEYLEN = 23, 22, AES_SET_KEY_LEN + # setting bit 21 breaks the engine and sets two bits in the IRQ status + ENCRYPT = 20, 20 + KEYGEN = 19, 18 + BLOCK_MODE = 17, 16, AES_SET_KEY_BLOCK_MODE + # 15, 0 doesn't seem to have any effect + + +class AESCryptCommand(AESCommandBase): + OPCODE = 31, 28, Constant(AES_OPCODE.CRYPT) + KEY_SLOT = 27, 27 + IV_SLOT = 26, 25 + LEN = 24, 0 + + +class AESBarrierCommand(AESCommandBase): + OPCODE = 31, 28, Constant(AES_OPCODE.BARRIER) + IRQ = 27, 27 + + +class AESGetIVCommand(AESCommandBase): + OPCODE = 31, 28, Constant(AES_OPCODE.GET_IV) + + +class AESSetIVCommand(AESCommandBase): + OPCODE = 31, 28, Constant(AES_OPCODE.SET_IV) + SLOT = 27, 26 + + +class AESIrqReg(Register32): + KEY1_EMPTY = 17, 17 + KEY1_INVALID = 13, 13 + KEY0_EMPTY = 11, 11 + KEY0_INVALID = 7, 7 + FLAG = 5, 5 + UNKNOWN_COMMAND = 2, 2 + FIFO_OVERFLOW = 1, 1 + + +class AESControlReg(Register32): + START = 0, 0 + STOP = 1, 1 + CLEAR_FIFO = 2, 2 + # TOOD: not convinced about RESET anymore, I remember this un-broke the engine once but I can't reproduce that anymore + RESET = 3, 3 + + +class AESFifoStatusReg(Register32): + FIFO_WRITE_PTR = 31, 24 + FIFO_READ_PTR = 23, 16 + FIFO_LEVEL = 15, 8 + FIFO_FULL = 2, 2 + FIFO_EMPTY = 1, 1 + + +class AESRegs(RegMap): + R_CONTROL = 0x08, AESControlReg + R_IRQ_STATUS = 0x18, AESIrqReg + R_IRQ_ENABLE = 0x1C, AESIrqReg + R_FIFO_STATUS = 0x24, AESFifoStatusReg + R_CMD_FIFO = 0x200, Register32 diff --git a/tools/proxyclient/m1n1/hw/agx.py b/tools/proxyclient/m1n1/hw/agx.py new file mode 100644 index 0000000..e5c2daa --- /dev/null +++ b/tools/proxyclient/m1n1/hw/agx.py @@ -0,0 +1,111 @@ +# SPDX-License-Identifier: MIT +from ..utils import * +from enum import IntEnum + +__all__ = ["SGXRegs", "SGXInfoRegs", "agx_decode_unit", "R_FAULT_INFO"] + +class FAULT_REASON(IntEnum): + INVALID = 0 + AF_FAULT = 1 + WRITE_ONLY = 2 + READ_ONLY = 3 + NO_ACCESS = 4 + UNK = 5 + +class R_FAULT_INFO(Register64): + ADDR = 63, 24 + WRITE = 23 + CONTEXT = 22, 17 + UNIT = 16, 9 + UNK_8 = 8 + REASON = 3, 1, FAULT_REASON + FAULTED = 0 + +class SGXRegs(RegMap): + FAULT_INFO = 0x17030, R_FAULT_INFO + +class SGXInfoRegs(RegMap): + CORE_MASK_0 = 0x1500, Register32, + CORE_MASK_1 = 0x1514, Register32, + + ID_00 = 0x4000, Register32, + ID_04 = 0x4004, Register32, + ID_08 = 0x4008, Register32, + ID_0c = 0x400c, Register32, + ID_10 = 0x4010, Register32, + ID_14 = 0x4014, Register32, + ID_18 = 0x4018, Register32, + ID_1c = 0x401c, Register32, + + ID_8024 = 0x8024, Register32, + +class UNIT_00(IntEnum): + DCMPn = 0x00 + UL1Cn = 0x01 + CMPn = 0x02 + GSL1_n = 0x03 + IAPn = 0x04 + VCEn = 0x05 + TEn = 0x06 + RASn = 0x07 + VDMn = 0x08 + PPPn = 0x09 + IPFn = 0x0a + IPF_CPFn = 0x0b + VFn = 0x0c + VF_CPFn = 0x0d + ZLSn = 0x0e + +class UNIT_A0(IntEnum): + dPM = 0xa1 + dCDM_KS0 = 0xa2 + dCDM_KS1 = 0xa3 + dCDM_KS2 = 0xa4 + dIPP = 0xa5 + dIPP_CS = 0xa6 + dVDM_CSD = 0xa7 + dVDM_SSD = 0xa8 + dVDM_ILF = 0xa9 + dVDM_ILD = 0xaa + dRDE0 = 0xab + dRDE1 = 0xac + FC = 0xad + GSL2 = 0xae + + GL2CC_META0 = 0xb0 + GL2CC_META1 = 0xb1 + GL2CC_META2 = 0xb2 + GL2CC_META3 = 0xb3 + GL2CC_META4 = 0xb4 + GL2CC_META5 = 0xb5 + GL2CC_META6 = 0xb6 + GL2CC_META7 = 0xb7 + GL2CC_MB = 0xb8 + +class UNIT_E0(IntEnum): + gPM_SPn = 0xe0 + gVDM_CSD_SPn = 0xe1 + gVDM_SSD_SPn = 0xe2 + gVDM_ILF_SPn = 0xe3 + gVDM_TFP_SPn = 0xe4 + gVDM_MMB_SPn = 0xe5 + gCDM_CS_SPn_KS0 = 0xe6 + gCDM_CS_SPn_KS1 = 0xe7 + gCDM_CS_SPn_KS2 = 0xe8 + gCDM_SPn_KS0 = 0xe9 + gCDM_SPn_KS1 = 0xea + gCDM_SPn_KS2 = 0xeb + gIPP_SPn = 0xec + gIPP_CS_SPn = 0xed + gRDE0_SPn = 0xee + gRDE1_SPn = 0xef + +def agx_decode_unit(v): + if v < 0xa0: + group = v >> 4 + return UNIT_00(v & 0x0f).name.replace("n", str(group)) + elif v < 0xe0: + return UNIT_A0(v).name + else: + group = (v >> 4) & 1 + return UNIT_E0(v & 0xef).name.replace("n", str(group)) diff --git a/tools/proxyclient/m1n1/hw/asc.py b/tools/proxyclient/m1n1/hw/asc.py new file mode 100644 index 0000000..f0923f8 --- /dev/null +++ b/tools/proxyclient/m1n1/hw/asc.py @@ -0,0 +1,121 @@ +# SPDX-License-Identifier: MIT +from ..utils import * +import time + +class R_MBOX_CTRL(Register32): + FIFOCNT = 23, 20 + OVERFLOW = 18 + EMPTY = 17 + FULL = 16 + RPTR = 15, 12 + WPTR = 11, 8 + ENABLE = 0 + +class R_CPU_CONTROL(Register32): + RUN = 4 + +class R_CPU_STATUS(Register32): + IDLE = 5 + FIQ_NOT_PEND = 3 # guess + IRQ_NOT_PEND = 2 # guess + STOPPED = 1 + RUNNING = 0 + +class R_INBOX1(Register64): + EP = 7, 0 + +class R_OUTBOX1(Register64): + OUTCNT = 56, 52 + INCNT = 51, 48 + OUTPTR = 47, 44 + INPTR = 43, 40 + EP = 7, 0 + +class ASCRegs(RegMap): + CPU_CONTROL = 0x0044, R_CPU_CONTROL + CPU_STATUS = 0x0048, R_CPU_STATUS + + INBOX_CTRL = 0x8110, R_MBOX_CTRL + OUTBOX_CTRL = 0x8114, R_MBOX_CTRL + INBOX0 = 0x8800, Register64 + INBOX1 = 0x8808, R_INBOX1 + OUTBOX0 = 0x8830, Register64 + OUTBOX1 = 0x8838, R_OUTBOX1 + +class ASC: + def __init__(self, u, asc_base): + self.u = u + self.p = u.proxy + self.iface = u.iface + self.asc = ASCRegs(u, asc_base) + self.verbose = 0 + self.epmap = {} + + def recv(self): + if self.asc.OUTBOX_CTRL.reg.EMPTY: + return None, None + + msg0 = self.asc.OUTBOX0.val + msg1 = R_INBOX1(self.asc.OUTBOX1.val) + if self.verbose >= 3: + print(f"< {msg1.EP:02x}:{msg0:#x}") + return msg0, msg1 + + def send(self, msg0, msg1): + self.asc.INBOX0.val = msg0 + self.asc.INBOX1.val = msg1 + + if self.verbose >= 3: + if isinstance(msg0, Register): + print(f"> {msg1.EP:02x}:{msg0}") + else: + print(f"> {msg1.EP:02x}:{msg0:#x}") + + while self.asc.INBOX_CTRL.reg.FULL: + pass + + def is_running(self): + return not self.asc.CPU_STATUS.reg.STOPPED + + def boot(self): + self.asc.CPU_CONTROL.set(RUN=1) + + def shutdown(self): + self.asc.CPU_CONTROL.set(RUN=0) + + def add_ep(self, idx, ep): + self.epmap[idx] = ep + setattr(self, ep.SHORT, ep) + + def has_messages(self): + return not self.asc.OUTBOX_CTRL.reg.EMPTY + + def work_pending(self): + while self.has_messages(): + self.work() + + def work(self): + if self.asc.OUTBOX_CTRL.reg.EMPTY: + return True + + msg0, msg1 = self.recv() + + handled = False + + ep = self.epmap.get(msg1.EP, None) + if ep: + handled = ep.handle_msg(msg0, msg1) + + if not handled: + print(f"unknown message: {msg0:#16x} / {msg1}") + + return handled + + def work_forever(self): + while self.work(): + pass + + def work_for(self, timeout): + deadline = time.time() + timeout + while time.time() < deadline: + self.work() diff --git a/tools/proxyclient/m1n1/hw/atc.py b/tools/proxyclient/m1n1/hw/atc.py new file mode 100644 index 0000000..7ae92c0 --- /dev/null +++ b/tools/proxyclient/m1n1/hw/atc.py @@ -0,0 +1,455 @@ +# SPDX-License-Identifier: MIT + +from enum import IntEnum +from m1n1.utils import * + + +class R_USB2PHY_USBCTL(Register32): + MODE_HOST = 1 + MODE_ISOLATION = 2 + + +class R_USB2PHY_CTL(Register32): + RESET = 0 + PORT_RESET = 1 + APB_RESETN = 2 + SIDDQ = 3 + + +class R_USB2PHY_SIG(Register32): + VBUSDET_FORCE_VAL = 0 + VBUSDET_FORCE_EN = 1 + VBUSVLDEXT_FORCE_VAL = 2 + VBUSVLDEXT_FORCE_EN = 3 + MODE_HOST = 19, 12 + + +class R_USB2PHY_MISCTUNE(Register32): + APBCLK_GATE_OFF = 29 + REFCLK_GATE_OFF = 30 + + +class Usb2PhyRegs(RegMap): + USB2PHY_USBCTL = 0x00, R_USB2PHY_USBCTL + USB2PHY_CTL = 0x04, R_USB2PHY_CTL + USB2PHY_SIG = 0x08, R_USB2PHY_SIG + USB2PHY_MISCTUNE = 0x1C, R_USB2PHY_MISCTUNE + + +class R_AUSPLL_DCO_EFUSE_SPARE(Register32): + RODCO_ENCAP_EFUSE = 10, 9 + RODCO_BIAS_ADJUST_EFUSE = 14, 12 + + +class R_AUSPLL_FRACN_CAN(Register32): + DLL_START_CAPCODE = 18, 17 + + +class R_AUSPLL_FSM_CTRL(Register32): + APBREQ_OVSEL = 21, 13 + + +class R_AUSPLL_CMD_OVERRIDE(Register32): + APB_OVERRIDE = 28 + + +class R_AUSPLL_CLKOUT_DTC_VREG(Register32): + DTC_VREG_ADJUST = 16, 14 + + +class R_AUS_COMMON_SHIM_BLK_VREG(Register32): + VREG_TRIM = 6, 2 + + +class R_CIO3PLL_CLK_CTRL(Register32): + PCLK_EN = 1 + REFCLK_EN = 5 + + +class R_CIO3PLL_DCO_NCTRL(Register32): + DCO_COARSEBIN_EFUSE0 = 6, 0 + DCO_COARSEBIN_EFUSE1 = 23, 17 + + +class R_CIO3PLL_FRACN_CAN(Register32): + DLL_CAL_START_CAPCODE = 18, 17 + + +class R_CIO3PLL_DTC_VREG(Register32): + DTC_VREG_ADJUST = 16, 14 + + +class E_ACIOPHY_CROSSBAR_PROTOCOL(IntEnum): + USB4 = 0 + USB3 = 5 + USB3_DP = 8 + DP = 10 + + +class R_ACIOPHY_CROSSBAR(Register32): + PROTOCOL_SWAPPED = 0 + PROTOCOL = 4, 1, E_ACIOPHY_CROSSBAR_PROTOCOL + DPMODE = 17, 5 + + +class E_ACIOPHY_LANE_MODE(IntEnum): + USB4 = 0 + USB3 = 1 + DP = 2 + OFF = 3 + + +class R_ACIOPHY_LANE_MODE(Register32): + RX0 = 2, 0, E_ACIOPHY_LANE_MODE + TX0 = 5, 3, E_ACIOPHY_LANE_MODE + RX1 = 8, 6, E_ACIOPHY_LANE_MODE + TX1 = 11, 9, E_ACIOPHY_LANE_MODE + + +class R_ATCPHY_POWER(Register32): + SLEEP_SMALL = 0 + SLEEP_BIG = 1 + CLAMP_EN = 2 + APB_RESET_N = 3 + PHY_RESET_N = 4 + + +class R_ATCPHY_MISC(Register32): + RESET_N = 0 + LANE_SWAP = 2 + + +class R_AUSPMA_TX_SHM_TXA_CFG_MAIN_REG0(Register32): + PMA_TXA_BYTECLK_RESET_SYNC_EN_OV = 2 + PMA_TXA_BYTECLK_RESET_SYNC_EN = 3 + PMA_TXA_BYTECLK_RESET_SYNC_CLR_OV = 4 + PMA_TXA_BYTECLK_RESET_SYNC_CLR = 5 + PMA_TXA_BYTECLK_RESET_SYNC_SEL_OV = 6 + + +class R_AUSPMA_TX_SHM_TXA_CFG_MAIN_REG1(Register32): + PMA_TXA_DIV2_EN_OV = 8 + PMA_TXA_DIV2_EN = 9 + PMA_TXA_DIV2_RESET_OV = 10 + PMA_TXA_DIV2_RESET = 11 + PMA_TXA_CLK_EN_OV = 22 + PMA_TXA_CLK_EN = 23 + + +class R_AUSPMA_TX_SHM_TXA_IMP_REG0(Register32): + PMA_TXA_CAL_CTRL_OV = 0 + PMA_TXA_CAL_CTRL = 18, 1 + PMA_TXA_CAL_CTRL_BASE_OV = 19 + PMA_TXA_CAL_CTRL_BASE = 23, 20 + PMA_TXA_HIZ_OV = 29 + PMA_TXA_HIZ = 30 + + +class R_AUSPMA_TX_SHM_TXA_IMP_REG2(Register32): + PMA_TXA_MARGIN_OV = 0 + PMA_TXA_MARGIN = 18, 1 + PMA_TXA_MARGIN_2R_OV = 19 + PMA_TXA_MARGIN_2R = 20 + + +class R_AUSPMA_TX_SHM_TXA_IMP_REG3(Register32): + PMA_TXA_MARGIN_POST_OV = 0 + PMA_TXA_MARGIN_POST = 10, 1 + PMA_TXA_MARGIN_POST_2R_OV = 11 + PMA_TXA_MARGIN_POST_2R = 12 + PMA_TXA_MARGIN_POST_4R_OV = 13 + PMA_TXA_MARGIN_POST_4R = 14 + PMA_TXA_MARGIN_PRE_OV = 15 + PMA_TXA_MARGIN_PRE = 21, 16 + PMA_TXA_MARGIN_PRE_2R_OV = 22 + PMA_TXA_MARGIN_PRE_2R = 23 + PMA_TXA_MARGIN_PRE_4R_OV = 24 + PMA_TXA_MARGIN_PRE_4R = 25 + + +class R_AUSPMA_TX_SHM_TXA_LDOCLK(Register32): + PMA_TXA_LDOCLK_BYPASS_SML_OV = 8 + PMA_TXA_LDOCLK_BYPASS_SML = 9 + PMA_TXA_LDOCLK_BYPASS_BIG_OV = 10 + PMA_TXA_LDOCLK_BYPASS_BIG = 11 + PMA_TXA_LDOCLK_EN_SML_OV = 12 + PMA_TXA_LDOCLK_EN_SML = 13 + PMA_TXA_LDOCLK_EN_BIG_OV = 14 + PMA_TXA_LDOCLK_EN_BIG = 15 + + +class R_AUSPMA_RX_SHM_TJ_RXA_CTLE_CTRL0(Register32): + PMA_RXA_TX_CLK_EN = 20 + PMA_RXA_TX_CLK_EN_OV = 21 + + +class R_AUSPMA_RX_SHM_TJ_RXA_AFE_CTRL1(Register32): + CLK_LANE_RX_DIV20_SYNC_RESET_N_OV = 29 + CLK_LANE_RX_DIV20_SYNC_RESET_N_VAL = 30 + + +class R_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL10(Register32): + PMA_RXA_DTVREG_ADJUST = 31, 27 + + +class R_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL11(Register32): + PMA_RXA_DTVREG_BIG_EN = 23 + PMA_RXA_DTVREG_BIG_EN_OV = 24 + PMA_RXA_DTVREG_SML_EN = 25 + PMA_RXA_DTVREG_SML_EN_OV = 26 + + +class R_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL12(Register32): + PMA_RXA_TX_BYTECLK_RESET_SYNC_CLR = 22 + PMA_RXA_TX_BYTECLK_RESET_SYNC_CLR_OV = 23 + PMA_RXA_TX_BYTECLK_RESET_SYNC_EN = 24 + PMA_RXA_TX_BYTECLK_RESET_SYNC_EN_OV = 25 + PMA_RXA_TX_HRCLK_SEL = 28 + PMA_RXA_TX_HRCLK_SEL_OV = 29 + PMA_RXA_TX_PBIAS_EN = 30 + PMA_RXA_TX_PBIAS_EN_OV = 31 + + +class R_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL13(Register32): + PMA_RXA_TX_PRE_EN = 0 + PMA_RXA_TX_PRE_EN_OV = 1 + PMA_RXA_TX_PST1_EN = 2 + PMA_RXA_TX_PST1_EN_OV = 3 + PMA_RXA_DTVREG_ADJUST_OV = 15 + + +class R_AUSPMA_RX_SHM_TJ_RXA_SAVOS_CTRL16(Register32): + PMA_RXA_RXTERM_EN = 21 + PMA_RXA_RXTERM_EN_OV = 22 + PMA_RXA_RXTERM_PULLUP_LEAK_EN = 23 + PMA_RXA_RXTERM_PULLUP_LEAK_EN_OV = 24 + PMA_RXA_TX_CAL_CODE = 29, 25 + PMA_RXA_TX_CAL_CODE_OV = 30 + + +class R_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL17(Register32): + PMA_RXA_TX_MARGIN = 19, 15 + PMA_RXA_TX_MARGIN_OV = 20 + PMA_RXA_TX_MARGIN_LSB = 21 + PMA_RXA_TX_MARGIN_LSB_OV = 22 + PMA_RXA_TX_MARGIN_P1 = 26, 23 + PMA_RXA_TX_MARGIN_P1_OV = 27 + PMA_RXA_TX_MARGIN_P1_LSB = 29, 28 + PMA_RXA_TX_MARGIN_P1_LSB_OV = 30 + + +class R_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL18(Register32): + PMA_RXA_TX_P1_CODE = 3, 0 + PMA_RXA_TX_P1_CODE_OV = 4 + PMA_RXA_TX_P1_LSB_CODE = 6, 5 + PMA_RXA_TX_P1_LSB_CODE_OV = 7 + PMA_RXA_TX_MARGIN_PRE = 10, 8 + PMA_RXA_TX_MARGIN_PRE_OV = 11 + PMA_RXA_TX_MARGIN_PRE_LSB = 13, 12 + PMA_RXA_TX_MARGIN_PRE_LSB_OV = 14 + PMA_RXA_TX_PRE_LSB_CODE = 16, 15 + PMA_RXA_TX_PRE_LSB_CODE_OV = 17 + PMA_RXA_TX_PRE_CODE = 21, 18 + PMA_RXA_TX_PRE_CODE_OV = 22 + + +class R_AUSPMA_RX_SHM_TJ_RXA_TERM_CTRL19(Register32): + PMA_RXA_TX_TEST_EN = 21 + PMA_RXA_TX_TEST_EN_OV = 22 + PMA_RXA_TX_EN = 23 + PMA_RXA_TX_EN_OV = 24 + PMA_RXA_TX_CLK_DLY_CTRL_TAPGEN = 27, 25 + PMA_RXA_TX_CLK_DIV2_EN = 28 + PMA_RXA_TX_CLK_DIV2_EN_OV = 29 + PMA_RXA_TX_CLK_DIV2_RST = 30 + PMA_RXA_TX_CLK_DIV2_RST_OV = 31 + + +class R_AUSPMA_RX_SHM_TJ_RXA_VREF_CTRL22(Register32): + PMA_RXA_VREF_ADJUST_GRAY = 11, 7 + PMA_RXA_VREF_ADJUST_GRAY_OV = 12 + PMA_RXA_VREF_BIAS_SEL = 14, 13 + PMA_RXA_VREF_BIAS_SEL_OV = 15 + PMA_RXA_VREF_BOOST_EN = 16 + PMA_RXA_VREF_BOOST_EN_OV = 17 + PMA_RXA_VREF_EN = 18 + PMA_RXA_VREF_EN_OV = 19 + LPBKIN_RECOVERED_DATA = 29, 28 + PMA_RXA_TEST_RXLPBKDT_EN = 30 + PMA_RXA_TEST_RXLPBKDT_EN_OV = 31 + + +class R_AUSPMA_RX_TOP_TJ_CFG_RX_TXMODE(Register32): + RX_TXMODE = 0 + +class R_ACIOPHY_LANE_DP_CFG_BLK_TX_DP_CTRL0(Register32): + DP_PMA_BYTECLK_RESET = 0 + DP_MAC_DIV20_CLK_SEL = 1 + DPTXPHY_PMA_LANE_RESET_N = 2 + DPTXPHY_PMA_LANE_RESET_N_OV = 3 + DPTX_PCLK1_SELECT = 6, 4 + DPTX_PCLK2_SELECT = 9, 7 + DPRX_PCLK_SELECT = 12, 10 + DPTX_PCLK1_ENABLE = 13 + DPTX_PCLK2_ENABLE = 14 + DPRX_PCLK_ENABLE = 15 + + +class AtcPhyRegs(RegMap): + ACIOPHY_CFG0 = 0x08, Register32 + ACIOPHY_LANE_MODE = 0x48, R_ACIOPHY_LANE_MODE + ACIOPHY_CROSSBAR = 0x4C, R_ACIOPHY_CROSSBAR + ACIOPHY_BIST_EN = 0x84, Register32 + ACIOPHY_BIST_OV = 0x8C, Register32 + ACIOPHY_BIST_CFG0 = 0x90, Register32 + ACIOPHY_BIST_STAT = 0x9C, Register32 + ACIOPHY_BIST_RESET = 0xA8, Register32 + ACIOPHY_BIST_CFG1 = 0xAC, Register32 + ACIOPHY_SLEEP_CTRL = 0x1B0, Register32 + + AUS_COMMON_SHIM_BLK_VREG = 0x0A04, R_AUS_COMMON_SHIM_BLK_VREG + + AUSPLL_FSM_CTRL = 0x1014, R_AUSPLL_FSM_CTRL + AUSPLL_CMD_OVERRIDE = 0x2000, R_AUSPLL_CMD_OVERRIDE + AUSPLL_CLKOUT_DTC_VREG = 0x2220, R_AUSPLL_CLKOUT_DTC_VREG + AUSPLL_DCO_EFUSE_SPARE = 0x222C, R_AUSPLL_DCO_EFUSE_SPARE + AUSPLL_FRACN_CAN = 0x22A4, R_AUSPLL_FRACN_CAN + + CIO3PLL_CLK_CTRL = 0x2A00, R_CIO3PLL_CLK_CTRL + CIO3PLL_DTC_VREG = 0x2A20, R_CIO3PLL_DTC_VREG + CIO3PLL_DCO_NCTRL = 0x2A38, R_CIO3PLL_DCO_NCTRL + CIO3PLL_FRACN_CAN = 0x2AA4, R_CIO3PLL_FRACN_CAN + + ATCPHY_POWER_CTRL = 0x20000, R_ATCPHY_POWER + ATCPHY_POWER_STAT = 0x20004, R_ATCPHY_POWER + ATCPHY_MISC = 0x20008, R_ATCPHY_MISC + + ACIOPHY_LANE_DP_CFG_BLK_TX_DP_CTRL0 = 0x7000, R_ACIOPHY_LANE_DP_CFG_BLK_TX_DP_CTRL0 + DPPHY_UNK_1028 = 0x1028, Register32 + USB2PHY_AUX_CFG_BLK_AUX_POWER_DOWN_CONTROL_0 = 0x54000, Register32 + + FABRIC_TUNABLES = irange(0x45000, 0x1000 // 4, 4), Register32 + + LPDPTX_AUX_CFG_BLK = irange(0x50000, 0x1000 // 4, 4), Register32 + LPDPTX_AUX_CFG_BLK_AUX_CTRL = 0x50000, Register32 + LPDPTX_AUX_CFG_BLK_AUX_LDO_CTRL = 0x50008, Register32 + LPDPTX_AUX_CFG_BLK_AUX_MARGIN = 0x5000c, Register32 + LPDPTX_AUX_SHM_CFG_BLK_AUX_CTRL_REG0 = 0x50204, Register32 + LPDPTX_AUX_SHM_CFG_BLK_AUX_CTRL_REG1 = 0x50208, Register32 + + LN0_AUSPMA_RX_TOP = irange(0x9000, 0x1000 // 4, 4), Register32 + LN0_AUSPMA_RX_TOP_TJ_CFG_RX_TXMODE = 0x90F0, R_AUSPMA_RX_TOP_TJ_CFG_RX_TXMODE + + LN0_AUSPMA_RX_EQ = irange(0xA000, 0x1000 // 4, 4), Register32 + + LN0_AUSPMA_RX_SHM = irange(0xB000, 0x1000 // 4, 4), Register32 + LN0_AUSPMA_RX_SHM_TJ_RXA_CTLE_CTRL0 = 0xB000, R_AUSPMA_RX_SHM_TJ_RXA_CTLE_CTRL0 + LN0_AUSPMA_RX_SHM_TJ_RXA_AFE_CTRL1 = 0xB004, R_AUSPMA_RX_SHM_TJ_RXA_AFE_CTRL1 + LN0_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL2 = 0xB008, Register32 + LN0_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL3 = 0xB00C, Register32 + LN0_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL4 = 0xB010, Register32 + LN0_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL5 = 0xB014, Register32 + LN0_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL6 = 0xB018, Register32 + LN0_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL7 = 0xB01C, Register32 + LN0_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL8 = 0xB020, Register32 + LN0_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL9 = 0xB024, Register32 + LN0_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL10 = 0xB028, R_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL10 + LN0_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL11 = 0xB02C, R_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL11 + LN0_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL12 = 0xB030, R_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL12 + LN0_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL13 = 0xB034, R_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL13 + LN0_AUSPMA_RX_SHM_TJ_UNK_CTRL14A = 0xB038, Register32 + LN0_AUSPMA_RX_SHM_TJ_UNK_CTRL14B = 0xB03C, Register32 + LN0_AUSPMA_RX_SHM_TJ_UNK_CTRL15A = 0xB040, Register32 + LN0_AUSPMA_RX_SHM_TJ_UNK_CTRL15B = 0xB044, Register32 + LN0_AUSPMA_RX_SHM_TJ_RXA_SAVOS_CTRL16 = 0xB048, R_AUSPMA_RX_SHM_TJ_RXA_SAVOS_CTRL16 + LN0_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL17 = 0xB04C, R_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL17 + LN0_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL18 = 0xB050, R_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL18 + LN0_AUSPMA_RX_SHM_TJ_RXA_TERM_CTRL19 = 0xB054, R_AUSPMA_RX_SHM_TJ_RXA_TERM_CTRL19 + LN0_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL20 = 0xB058, Register32 + LN0_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL21 = 0xB05C, Register32 + LN0_AUSPMA_RX_SHM_TJ_RXA_VREF_CTRL22 = 0xB060, R_AUSPMA_RX_SHM_TJ_RXA_VREF_CTRL22 + + LN0_AUSPMA_TX_TOP = irange(0xC000, 0x1000 // 4, 4), Register32 + + LN0_AUSPMA_TX_SHM = irange(0xD000, 0x1000 // 4, 4), Register32 + LN0_AUSPMA_TX_SHM_TXA_CFG_MAIN_REG0 = 0xD000, R_AUSPMA_TX_SHM_TXA_CFG_MAIN_REG0 + LN0_AUSPMA_TX_SHM_TXA_CFG_MAIN_REG1 = 0xD004, R_AUSPMA_TX_SHM_TXA_CFG_MAIN_REG1 + LN0_AUSPMA_TX_SHM_TXA_IMP_REG0 = 0xD008, R_AUSPMA_TX_SHM_TXA_IMP_REG0 + LN0_AUSPMA_TX_SHM_TXA_IMP_REG1 = 0xD00C, Register32 + LN0_AUSPMA_TX_SHM_TXA_IMP_REG2 = 0xD010, R_AUSPMA_TX_SHM_TXA_IMP_REG2 + LN0_AUSPMA_TX_SHM_TXA_IMP_REG3 = 0xD014, R_AUSPMA_TX_SHM_TXA_IMP_REG3 + LN0_AUSPMA_TX_SHM_TXA_UNK_REG0 = 0xD018, Register32 + LN0_AUSPMA_TX_SHM_TXA_UNK_REG1 = 0xD01C, Register32 + LN0_AUSPMA_TX_SHM_TXA_UNK_REG2 = 0xD020, Register32 + LN0_AUSPMA_TX_SHM_TXA_LDOCLK = 0xD024, R_AUSPMA_TX_SHM_TXA_LDOCLK + + LN1_AUSPMA_RX_TOP = irange(0x10000, 0x1000 // 4, 4), Register32 + LN1_AUSPMA_RX_TOP_TJ_CFG_RX_TXMODE = 0x100F0, R_AUSPMA_RX_TOP_TJ_CFG_RX_TXMODE + + LN1_AUSPMA_RX_EQ = irange(0x11000, 0x1000 // 4, 4), Register32 + + LN1_AUSPMA_RX_SHM = irange(0x12000, 0x1000 // 4, 4), Register32 + LN1_AUSPMA_RX_SHM_TJ_RXA_CTLE_CTRL0 = 0x12000, R_AUSPMA_RX_SHM_TJ_RXA_CTLE_CTRL0 + LN1_AUSPMA_RX_SHM_TJ_RXA_AFE_CTRL1 = 0x12004, R_AUSPMA_RX_SHM_TJ_RXA_AFE_CTRL1 + LN1_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL2 = 0x12008, Register32 + LN1_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL3 = 0x1200C, Register32 + LN1_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL4 = 0x12010, Register32 + LN1_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL5 = 0x12014, Register32 + LN1_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL6 = 0x12018, Register32 + LN1_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL7 = 0x1201C, Register32 + LN1_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL8 = 0x12020, Register32 + LN1_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL9 = 0x12024, Register32 + LN1_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL10 = 0x12028, R_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL10 + LN1_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL11 = 0x1202C, R_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL11 + LN1_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL12 = 0x12030, R_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL12 + LN1_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL13 = 0x12034, R_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL13 + LN1_AUSPMA_RX_SHM_TJ_UNK_CTRL14A = 0x12038, Register32 + LN1_AUSPMA_RX_SHM_TJ_UNK_CTRL14B = 0x1203C, Register32 + LN1_AUSPMA_RX_SHM_TJ_UNK_CTRL15A = 0x12040, Register32 + LN1_AUSPMA_RX_SHM_TJ_UNK_CTRL15B = 0x12044, Register32 + LN1_AUSPMA_RX_SHM_TJ_RXA_SAVOS_CTRL16 = 0x12048, R_AUSPMA_RX_SHM_TJ_RXA_SAVOS_CTRL16 + LN1_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL17 = 0x1204C, R_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL17 + LN1_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL18 = 0x12050, R_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL18 + LN1_AUSPMA_RX_SHM_TJ_RXA_TERM_CTRL19 = 0x12054, R_AUSPMA_RX_SHM_TJ_RXA_TERM_CTRL19 + LN1_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL20 = 0x12058, Register32 + LN1_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL21 = 0x1205C, Register32 + LN1_AUSPMA_RX_SHM_TJ_RXA_VREF_CTRL22 = 0x12060, R_AUSPMA_RX_SHM_TJ_RXA_VREF_CTRL22 + + LN1_AUSPMA_TX_TOP = irange(0x13000, 0x1000 // 4, 4), Register32 + + LN1_AUSPMA_TX_SHM = irange(0x14000, 0x1000 // 4, 4), Register32 + LN1_AUSPMA_TX_SHM_TXA_CFG_MAIN_REG0 = 0x14000, R_AUSPMA_TX_SHM_TXA_CFG_MAIN_REG0 + LN1_AUSPMA_TX_SHM_TXA_CFG_MAIN_REG1 = 0x14004, R_AUSPMA_TX_SHM_TXA_CFG_MAIN_REG1 + LN1_AUSPMA_TX_SHM_TXA_IMP_REG0 = 0x14008, R_AUSPMA_TX_SHM_TXA_IMP_REG0 + LN1_AUSPMA_TX_SHM_TXA_IMP_REG1 = 0x1400C, Register32 + LN1_AUSPMA_TX_SHM_TXA_IMP_REG2 = 0x14010, R_AUSPMA_TX_SHM_TXA_IMP_REG2 + LN1_AUSPMA_TX_SHM_TXA_IMP_REG3 = 0x14014, R_AUSPMA_TX_SHM_TXA_IMP_REG3 + LN1_AUSPMA_TX_SHM_TXA_UNK_REG0 = 0x14018, Register32 + LN1_AUSPMA_TX_SHM_TXA_UNK_REG1 = 0x1401C, Register32 + LN1_AUSPMA_TX_SHM_TXA_UNK_REG2 = 0x14020, Register32 + LN1_AUSPMA_TX_SHM_TXA_LDOCLK = 0x14024, R_AUSPMA_TX_SHM_TXA_LDOCLK + + ACIOPHY_TOP_TUNABLE_118 = 0x118, Register32 + ACIOPHY_TOP_TUNABLE_11c = 0x11C, Register32 + ACIOPHY_TOP_TUNABLE_124 = 0x124, Register32 + ACIOPHY_TOP_TUNABLE_a00 = 0xA00, Register32 + ACIOPHY_TOP_TUNABLE_808 = 0x808, Register32 + + AUSPLL_TOP_FREQ_DESC_0A = 0x2080, Register32 + AUSPLL_TOP_FREQ_DESC_0B = 0x2084, Register32 + AUSPLL_TOP_FREQ_DESC_0C = 0x2088, Register32 + + AUSPLL_TOP_TUNABLE_2094 = 0x2094, Register32 + AUSPLL_TOP_TUNABLE_20a0 = 0x20A0, Register32 + AUSPLL_TOP_TUNABLE_20ac = 0x20AC, Register32 + AUSPLL_TOP_TUNABLE_20b8 = 0x20B8, Register32 + + CIO3PLL_TOP_TUNABLE_10 = 0x2810, Register32 + CIO3PLL_TOP_TUNABLE_88 = 0x2888, Register32 + CIO3PLL_TOP_TUNABLE_94 = 0x2894, Register32 + + CIO3PLL_CORE_TUNABLE_1c = 0x2A1C, Register32 + CIO3PLL_CORE_TUNABLE_28 = 0x2A28, Register32 + CIO3PLL_CORE_TUNABLE_9c = 0x2A9C, Register32 + + AUSPLL_CORE_TUNABLE_78 = 0x2278, Register32 diff --git a/tools/proxyclient/m1n1/hw/codecs/__init__.py b/tools/proxyclient/m1n1/hw/codecs/__init__.py new file mode 100644 index 0000000..46bb5ee --- /dev/null +++ b/tools/proxyclient/m1n1/hw/codecs/__init__.py @@ -0,0 +1,109 @@ +from m1n1.utils import RegMap, Register8 +from enum import IntEnum +from .cs42l84 import * + +class E_PWR_MODE(IntEnum): + ACTIVE = 0 + MUTE = 1 + SHUTDOWN = 2 + +class R_PWR_CTL(Register8): + ISNS_PD = 3 + VSNS_PD = 2 + MODE = 1, 0, E_PWR_MODE + +class R_PB_CFG0(Register8): + PDM_MAP = 7 + PB_PDM_SRC = 6 + PB_SRC = 5 + AMP_LEVEL = 4, 0 + +class R_PB_CFG2(Register8): + DVC_PCM = 7, 0 + +class R_PB_CFG3(Register8): + DVC_PDM = 7, 0 + +class E_RX_SCFG(IntEnum): + I2C_OFFSET = 0b00 + LEFT = 0b01 + RIGHT = 0b10 + DOWNMIX = 0b11 + +class E_RX_WLEN(IntEnum): + W_16BIT = 0b00 + W_20BIT = 0b01 + W_24BIT = 0b10 + W_32BIT = 0b11 + +class E_RX_SLEN(IntEnum): + W_16BIT = 0b00 + W_24BIT = 0b01 + W_32BIT = 0b10 + +class R_TDM_CFG2(Register8): + RX_SCFG = 5, 4, E_RX_SCFG + RX_WLEN = 3, 2, E_RX_WLEN + RX_SLEN = 1, 0, E_RX_SLEN + +class R_TDM_CFG3(Register8): + RX_SLOT_R = 7, 4 + RX_SLOT_L = 3, 0 + +class TAS5770Regs(RegMap): + PWR_CTL = 0x002, R_PWR_CTL + PB_CFG0 = 0x003, R_PB_CFG0 + PB_CFG2 = 0x005, R_PB_CFG2 + PB_CFG3 = 0x006, R_PB_CFG3 + TDM_CFG2 = 0x00c, R_TDM_CFG2 + TDM_CFG3 = 0x00d, R_TDM_CFG3 + +class R_MODE_CTRL(Register8): + BOP_SRC = 7 + ISNS_PD = 3 + VSNS_PD = 2 + MODE = 1, 0, E_PWR_MODE + +class R_CHNL_0(Register8): + CDS_MODE = 7, 6 + AMP_LEVEL = 5, 1 + +class R_DVC(Register8): + DVC_LVL = 7, 0 + +class R_INT_MASK0(Register8): + BOPM = 7 + BOPIH = 6 + LIMMA = 5 + PBIP = 4 + LIMA = 3 + TDMCE = 2 + OC = 1 + OT = 0 + +class R_INT_CLK_CFG(Register8): + CLK_ERR_PWR_EN = 7 + DIS_CLK_HAT = 6 + CLK_HALT_TIMER = 5, 3 + IRQZ_CLR = 2 + IRQZ_PIN_CFG = 1, 0 + +class SN012776Regs(RegMap): + MODE_CTRL = 0x002, R_MODE_CTRL + CHNL_0 = 0x003, R_CHNL_0 + DVC = 0x01a, R_DVC + + INT_MASK0 = 0x03b, R_INT_MASK0 + INT_MASK1 = 0x03c, Register8 + INT_MASK2 = 0x040, Register8 + INT_MASK3 = 0x041, Register8 + INT_MASK4 = 0x03d, Register8 + + INT_LTCH0 = 0x049, R_INT_MASK0 + INT_LTCH1 = 0x04a, Register8 + INT_LTCH1_0 = 0x04b, Register8 + INT_LTCH2 = 0x04f, Register8 + INT_LTCH3 = 0x050, Register8 + INT_LTCH4 = 0x051, Register8 + + INT_CLK_CFG = 0x05c, R_INT_CLK_CFG diff --git a/tools/proxyclient/m1n1/hw/codecs/cs42l84.py b/tools/proxyclient/m1n1/hw/codecs/cs42l84.py new file mode 100644 index 0000000..095b421 --- /dev/null +++ b/tools/proxyclient/m1n1/hw/codecs/cs42l84.py @@ -0,0 +1,365 @@ +from m1n1.utils import Register8, Register16, Register32, RegMap, irange +from enum import IntEnum + +class R_IRQ_MASK1(Register8): + RING_PLUG = 0 + RING_UNPLUG = 1 + TIP_PLUG = 2 + TIP_UNPLUG = 3 + +class R_IRQ_MASK3(Register8): + HSDET_AUTO_DONE = 7 + +class E_DCID_GND_SEL(IntEnum): + NONE = 0 + HS3 = 1 + HS4 = 2 + +class E_DCID_Z_RANGE(IntEnum): + NONE = 0 + UNK2 = 2 + UNK3 = 3 + +class R_DCID_CTRL1(Register8): + Z_RANGE = 2, 0, E_DCID_Z_RANGE + +class R_DCID_CTRL2(Register8): + GND_SEL = 6, 4, E_DCID_GND_SEL + +class R_DCID_CTRL3(Register8): + START = 0 + +class R_DCID_STATUS(Register32): + OVERALL = 9, 0 + DONE = 10 + U = 20, 11 + D = 30, 21 + +class E_DEBOUNCE_TIME(IntEnum): + T_0MS = 0b000 + T_125MS = 0b001 + T_250MS = 0b010 + T_500MS = 0b011 + T_750MS = 0b100 + T_1S = 0b101 + +class R_TR_SENSE_CTRL(Register8): + INV = 7 + UNK1 = 6 + FALLTIME = 5, 3, E_DEBOUNCE_TIME + RISETIME = 2, 0, E_DEBOUNCE_TIME + +class R_TR_SENSE_STATUS(Register8): + RING_PLUG = 0 + RING_UNPLUG = 1 + TIP_PLUG = 2 + TIP_UNPLUG = 3 + +class R_HS_DET_STATUS2(Register8): + HS_TRUE = 1 + SHORT_TRUE = 0 + +class R_MSM_BLOCK_EN1(Register8): + pass + +class R_MSM_BLOCK_EN2(Register8): + ASP_EN = 6 + BUS_EN = 5 + DAC_EN = 4 + ADC_EN = 3 + +class R_MSM_BLOCK_EN3(Register8): + TR_SENSE_EN = 3 + DCID_EN = 4 + +class R_HS_CLAMP_DISABLE(Register8): + HS_CLAMP_DISABLE = 0 + +class E_SAMP_RATE(IntEnum): + S_16KHZ = 1 + S_24KHZ = 2 + S_32KHZ = 3 + S_48KHZ = 4 + S_96KHZ = 5 + S_192KHZ = 6 + S_22K05HZ = 10 + S_44K1HZ = 12 + S_88K2HZ = 13 + S_176K4HZ = 14 + +class E_MCLK_SRC(IntEnum): + RCO = 0b00 + MCLK_PIN = 0b01 + BCLK = 0b10 + PLL = 0b11 + +class E_MCLK_FREQ(IntEnum): + F_12MHZ = 0b00 + F_24MHZ = 0b01 + F_12_288KHZ = 0b10 + F_24_576KHZ = 0b11 + +class R_CCM_CTRL1(Register8): + MCLK_SRC = 1, 0, E_MCLK_SRC + MCLK_FREQ = 3, 2, E_MCLK_FREQ + +class E_REFCLK_DIV(IntEnum): + DIV1 = 0b00 + DIV2 = 0b01 + DIV4 = 0b10 + DIV8 = 0b11 + +class R_CCM_CTRL3(Register8): + REFCLK_DIV = 2, 1, E_REFCLK_DIV + REFCLK_IS_MCLK = 0 # BLCK otherwise + +class R_CCM_CTRL4(Register8): + REFCLK_EN = 0 + +class R_CCM_SAMP_RATE(Register8): + RATE = 7, 0, E_SAMP_RATE + +class E_PLL_MODE(IntEnum): + UNSUPP = 0b00 + BYPASS_512 = 0b01 + BYPASS_1024 = 0b10 + BYPASS_BOTH = 0b11 + +class R_PLL_CTRL(Register8): + MODE = 2, 1, E_PLL_MODE + EN = 0 + +class E_WNF_CF(IntEnum): + F_UNK = 0b00 + F_300HZ = 0b11 + +class R_ADC_CTRL1(Register8): + PREAMP_GAIN = 7, 6 + PGA_GAIN = 5, 0 + +class R_ADC_CTRL4(Register8): # maybe + WNF_CF = 5, 4, E_WNF_CF + WNF_EN = 3 + +class R_DAC_CTRL1(Register8): + UNMUTE = 0 + + HP_LOAD = 2 # maybe + UNK1 = 3 + UNK2 = 4 + UNK3 = 5 + HIGH_V = 6 + +class E_PULLDOWN_R(IntEnum): + NONE = 0x0 + R_UNK8 = 0x8 + R_1K1OHMS = 0xc + +class R_DAC_CTRL2(Register8): + PULLDOWN_R = 3, 0, E_PULLDOWN_R + +class R_HP_VOL_CTRL(Register8): + ZERO_CROSS = 1 + SOFT = 0 + +class E_BUS_SOURCE(IntEnum): + EMPTY = 0b0000 + ADC = 0b0111 + ASP_RX_CH1 = 0b1101 + ASP_RX_CH2 = 0b1110 + +class R_BUS_DAC_SRC(Register8): + CHB = 7, 4, E_BUS_SOURCE + CHA = 3, 0, E_BUS_SOURCE + +class R_BUS_ASP_TX_SRC(Register8): + CH2 = 7, 4, E_BUS_SOURCE + CH1 = 3, 0, E_BUS_SOURCE + +class E_HSBIAS_SENSE_TRIP(IntEnum): + C_12UA = 0b000 + C_23UA = 0b001 + C_41UA = 0b010 + C_52UA = 0b011 + C_64UA = 0b100 + C_75UA = 0b101 + C_93UA = 0b110 + C_104UA = 0b111 + +class R_HSBIAS_SC_AUTOCTL(Register8): + HSBIAS_SENSE_EN = 7 + AUTO_HSBIAS_HIZ = 6 + TIP_SENSE_EN = 5 + SENSE_TRIP = 2, 0, E_HSBIAS_SENSE_TRIP + +class E_TIP_SENSE_CTRL(IntEnum): + DISABLED = 0b00 + DIG_INPUT = 0b01 + SHORT_DET = 0b11 + +class R_TIP_SENSE_CTRL2(Register8): + CTRL = 7, 6, E_TIP_SENSE_CTRL + INV = 5 + +class E_HSBIAS_DET_MODE(IntEnum): + DISABLED = 0b00 + SHORT_DET = 0b01 + NORMAL = 0b11 + +class E_HSBIAS_CTRL(IntEnum): + HI_Z = 0b00 + U_0V0 = 0b01 + U_2V0 = 0b10 + U_2V7 = 0b11 + +class R_MISC_DET_CTRL(Register8): + UNK1 = 7 + DETECT_MODE = 4, 3, E_HSBIAS_DET_MODE + HSBIAS_CTRL = 2, 1, E_HSBIAS_CTRL + PDN_MIC_LVL_DET = 0 + +class E_S0_DEBOUNCE_TIME(IntEnum): + T_10MS = 0b000 + T_20MS = 0b001 + T_30MS = 0b010 + T_40MS = 0b011 + T_50MS = 0b100 + T_60MS = 0b101 + T_70MS = 0b110 + T_80MS = 0b111 + +class R_MIC_DET_CTRL2(Register8): + DEBOUNCE_TIME = 7, 5, E_S0_DEBOUNCE_TIME + +class R_MIC_DET_CTRL4(Register8): + LATCH_TO_VP = 1 + +class R_HS_DET_CTRL2(Register8): + CTRL = 7, 6 + SET = 5, 4 + REF = 3 + AUTO_TIME = 1, 0 + +class R_HS_SWITCH_CTRL(Register8): + REF_HS3 = 7 + REF_HS4 = 6 + HSB_FILT_HS3 = 5 + HSB_FILT_HS4 = 4 + HSB_HS3 = 3 + HSB_HS4 = 2 + GNDHS_HS3 = 1 + GNDHS_HS4 = 0 + +class R_ASP_CTRL(Register8): + TDM_MODE = 2 + BCLK_EN = 1 + +class R_ASP_FSYNC_CTRL23(Register16): + BCLK_PERIOD = 12, 1 + +class R_ASP_TX_HIZ_DLY_CTRL(Register8): + DRV_Z = 5, 4 + HIZ_DELAY = 3, 2 + FS = 1 + UNK1 = 0 + +class R_ASP_RX_EN(Register8): + CH2_EN = 1 + CH1_EN = 0 + +class R_ASP_CH_CTRL(Register32): + WIDTH = 23, 16 + SLOT_START = 10, 1 + EDGE = 0 # set for rising edge + +class CS42L84Regs(RegMap): + DEVID = irange(0x0, 5), Register8 + FREEZE = 0x6, Register8 + + SW_RESET = 0x203, Register8 + + IRQ_STATUS1 = 0x400, R_IRQ_MASK1 + IRQ_STATUS2 = 0x401, Register8 + IRQ_STATUS3 = 0x402, R_IRQ_MASK3 + PLL_LOCK_STATUS = 0x40e, Register8 # bit 0x10 + + IRQ_MASK1 = 0x418, R_IRQ_MASK1 + IRQ_MASK2 = 0x419, Register8 + IRQ_MASK3 = 0x41a, R_IRQ_MASK3 + + CCM_CTRL1 = 0x600, R_CCM_CTRL1 + CCM_SAMP_RATE = 0x601, R_CCM_SAMP_RATE + CCM_CTRL3 = 0x602, R_CCM_CTRL3 + CCM_CTRL4 = 0x603, R_CCM_CTRL4 + CCM_ASP_CLK_CTRL = 0x608, Register8 + + PLL_CTRL = 0x800, R_PLL_CTRL + PLL_DIV_FRAC = irange(0x804, 3), Register8 + PLL_DIV_INT = 0x807, Register8 + PLL_DIVOUT = 0x808, Register8 + + DCID_CTRL1 = 0x1200, R_DCID_CTRL1 + DCID_CTRL2 = 0x1201, R_DCID_CTRL2 + DCID_CTRL3 = 0x1202, R_DCID_CTRL3 + DCID_TRIM_OFFSET = 0x1207, Register8 + DCID_TRIM_SLOPE = 0x120a, Register8 + + # R_pull = 1100 - (regval - 128)*2 + DCID_PULLDOWN_TRIM = 0x120b, Register8 + DCID_STATUS = 0x120c, R_DCID_STATUS + + # tip/ring sense + TR_SENSE_CTRL1 = 0x1280, Register8 + TR_SENSE_CTRL2 = 0x1281, Register8 + RING_SENSE_CTRL = 0x1282, R_TR_SENSE_CTRL + TIP_SENSE_CTRL = 0x1283, R_TR_SENSE_CTRL + TR_SENSE_STATUS = 0x1288, R_TR_SENSE_STATUS + + HSBIAS_SC_AUTOCTL = 0x1470, R_HSBIAS_SC_AUTOCTL + WAKE_CTRL = 0x1471, Register8 + TIP_SENSE_CTRL2 = 0x1473, R_TIP_SENSE_CTRL2 + MISC_DET_CTRL = 0x1474, R_MISC_DET_CTRL + MIC_DET_CTRL2 = 0x1478, R_MIC_DET_CTRL2 + MIC_DET_CTRL4 = 0x1477, R_MIC_DET_CTRL4 + + HS_DET_STATUS1 = 0x147c, Register8 + HS_DET_STATUS2 = 0x147d, R_HS_DET_STATUS2 + HS_DET_IRQ_MASK = irange(0x1480, 2), Register8 + HS_DET_IRQ_STATUS = irange(0x1484, 2), Register8 + + MSM_BLOCK_EN1 = 0x1800, R_MSM_BLOCK_EN1 + MSM_BLOCK_EN2 = 0x1801, R_MSM_BLOCK_EN2 + MSM_BLOCK_EN3 = 0x1802, R_MSM_BLOCK_EN3 + + HS_DET_CTRL1 = 0x1810, Register8 + HS_DET_CTRL2 = 0x1811, R_HS_DET_CTRL2 + HS_SWITCH_CTRL = 0x1812, R_HS_SWITCH_CTRL + HS_CLAMP_DISABLE = 0x1813, R_HS_CLAMP_DISABLE + ADC_CTRL1 = 0x2000, R_ADC_CTRL1 + ADC_CTRL2 = 0x2001, Register8 # volume + ADC_CTRL3 = 0x2002, Register8 + ADC_CTRL4 = 0x2003, R_ADC_CTRL4 + + DAC_CTRL1 = 0x3000, R_DAC_CTRL1 + DAC_CTRL2 = 0x3001, R_DAC_CTRL2 + DACA_VOL_LSB = 0x3004, Register8 + DACA_VOL_MSB = 0x3005, Register8 # sign bit + DACB_VOL_LSB = 0x3006, Register8 + DACB_VOL_MSB = 0x3007, Register8 # sign bit + HP_VOL_CTRL = 0x3020, R_HP_VOL_CTRL + HP_CLAMP_CTRL = 0x3123, Register8 + + BUS_ASP_TX_SRC = 0x4000, R_BUS_ASP_TX_SRC + BUS_DAC_SRC = 0x4001, R_BUS_DAC_SRC + + ASP_CTRL = 0x5000, R_ASP_CTRL + ASP_FSYNC_CTRL23 = 0x5010, R_ASP_FSYNC_CTRL23 + ASP_DATA_CTRL = 0x5018, R_ASP_TX_HIZ_DLY_CTRL + + ASP_RX_EN = 0x5020, R_ASP_RX_EN + ASP_TX_EN = 0x5024, Register8 + + ASP_RX1_CTRL = 0x5028, R_ASP_CH_CTRL # 32bit + ASP_RX2_CTRL = 0x502c, R_ASP_CH_CTRL # 32bit + ASP_TX1_CTRL = 0x5068, R_ASP_CH_CTRL + ASP_TX2_CTRL = 0x506c, R_ASP_CH_CTRL diff --git a/tools/proxyclient/m1n1/hw/dart.py b/tools/proxyclient/m1n1/hw/dart.py new file mode 100644 index 0000000..cd3d8d7 --- /dev/null +++ b/tools/proxyclient/m1n1/hw/dart.py @@ -0,0 +1,102 @@ +# SPDX-License-Identifier: MIT + +import struct + +from enum import IntEnum +from ..utils import * +from ..malloc import Heap + +from .dart8020 import DART8020, DART8020Regs +from .dart8110 import DART8110, DART8110Regs + +__all__ = ["DART"] + +class DART(Reloadable): + PAGE_BITS = 14 + PAGE_SIZE = 1 << PAGE_BITS + + def __init__(self, iface, regs, util=None, compat="dart,t8020", iova_range=(0x80000000, 0x90000000)): + self.iface = iface + self.iova_allocator = [Heap(iova_range[0], iova_range[1], self.PAGE_SIZE) + for i in range(16)] + 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] + 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: + 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): + self.dart.iomap_at(stream, iova, addr, size) + + def iotranslate(self, stream, start, size): + return self.dart.iotranslate(stream, start, size) + + def initialize(self): + self.dart.initialize() + + def show_error(self): + self.dart.show_error() + + def invalidate_streams(self, streams=0xffffffff): + self.dart.invalidate_streams(streams) + + def invalidate_cache(self): + self.dart.invalidate_cache() + + def dump_device(self, idx): + 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() diff --git a/tools/proxyclient/m1n1/hw/dart8020.py b/tools/proxyclient/m1n1/hw/dart8020.py new file mode 100644 index 0000000..9954d3c --- /dev/null +++ b/tools/proxyclient/m1n1/hw/dart8020.py @@ -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 diff --git a/tools/proxyclient/m1n1/hw/dart8110.py b/tools/proxyclient/m1n1/hw/dart8110.py new file mode 100644 index 0000000..1655182 --- /dev/null +++ b/tools/proxyclient/m1n1/hw/dart8110.py @@ -0,0 +1,541 @@ +# SPDX-License-Identifier: MIT + +import struct + +from enum import IntEnum +from ..utils import * +from ..malloc import Heap + +__all__ = ["DART8110Regs", "DART8110"] + +class R_PARAMS_0(Register32): + CLIENT_PARTITIONS_SUPPORTED = 29 + LOG2_PGSZ = 27, 24 + LOG2_TE_COUNT = 22, 20 + TLB_SET_COUNT = 11, 0 + +class R_PARAMS_4(Register32): + LOG2_NUM_WAYS = 30, 28 + NUM_ASCS = 25, 24 + NUM_W_PORTS = 22, 20 + NUM_R_PORTS = 18, 16 + NUM_APFS = 15, 8 + SUPPORT_STT_PREFETCH = 6 + SUPPORT_TLB_PREFETCH = 5 + SUPPORT_CTC_PREFETCH = 4 + SUPPORT_HW_FLUSH = 3 + SUPPORT_TZ_TAGGER = 2 + SUPPORT_REG_LOCK = 1 + SUPPORT_FULL_BYPASS = 0 + +class R_PARAMS_8(Register32): + PA_WIDTH = 29, 24 + VA_WIDTH = 21, 16 + VERS_MAJ = 15, 8 + VERS_MIN = 7, 0 + +class R_PARAMS_C(Register32): + NUM_CLIENTS = 24, 16 + NUM_SIDS = 8, 0 + +class R_ERROR(Register32): + FLAG = 31 + SMMU = 30 + REGION_PROTECT = 29 + WRITE_nREAD = 28 + SID = 27, 20 + SECONDARY = 19 + FILL_REGION = 18 + BPF_REJECT = 14 + EXTERNAL = 13 + STT_FLUSH = 12 + STT_MISMATCH = 11 + APF_REJECT = 10 + DROP_PROTECT = 9 + CTRR_WRITE_PROTECT = 8 + AXI_ERROR = 7 + AXI_DECODE = 6 + READ_FAULT = 5 + WRITE_FAULT = 4 + NO_PTE = 3 + NO_PMD = 2 # "STE" + NO_PGD = 1 # "CTE" + NO_TTBR = 0 + +class R_TLB_OP(Register32): + BUSY = 31 + + # None of these bits are supported on hwrev 1 + HARDWARE_FLUSH = 30 + FLUSH_VA_RANGE = 14 + ENABLE_STT_FLUSH = 13 + DISABLE_STC_FLUSH = 12 + + # 0 = flush all + # 1 = flush SID + # 2 = TLB read + # 3 = TLB write???? + # 4 = flush unlock, definitely not supported on hwrev 1 + OP = 10, 8 + STREAM = 7, 0 + +class R_TLB_OP_IDX(Register32): + SET = 13, 8 + WAY = 6, 4 + TE = 2, 0 + +class R_PROTECT(Register32): + LOCK_TZ_SELECT = 4 + LOCK_TZ_CONFIG = 3 + # This bit can be set, but unknown what it protects + _BIT2 = 2 + LOCK_REG_4xx = 1 + LOCK_TCR_TTBR = 0 + +class R_DIAG_LOCK(Register32): + # FIXME: how does this work exactly? + LOCK_ON_ERR = 1 + LOCK = 0 + +class R_TCR(Register32): + REMAP = 11, 8 + REMAP_EN = 7 + FOUR_LEVELS = 3 # not supported on hwrev 1 + BYPASS_DAPF = 2 + BYPASS_DART = 1 + TRANSLATE_ENABLE = 0 + +class R_TTBR(Register32): + ADDR = 29, 2 + VALID = 0 + +class PTE(Register64): + SP_START = 63, 52 + SP_END = 51, 40 + OFFSET = 37, 10 + RDPROT = 3 + WRPROT = 2 + UNCACHABLE = 1 + VALID = 0 + +class DART8110Regs(RegMap): + PARAMS_0 = 0x000, R_PARAMS_0 + PARAMS_4 = 0x004, R_PARAMS_4 + PARAMS_8 = 0x008, R_PARAMS_8 + PARAMS_C = 0x00C, R_PARAMS_C + # Unknown RO + REG_0x10 = 0x010, Register32 + REG_0x14 = 0x014, Register32 # hwrev 2 only + + TLB_OP = 0x080, R_TLB_OP + TLP_OP_IDX = 0x084, R_TLB_OP_IDX + TLB_TAG_LO = 0x088, Register32 + TLB_TAG_HI = 0x08c, Register32 # hwrev 2 only + TLB_PA_LO = 0x090, Register32 + TLB_PA_HI = 0x094, Register32 + TLB_START_DVA_PAGE = 0x098, Register32 # hwrev 2 only + TLB_END_DVA_PAGE = 0x0a0, Register32 # hwrev 2 only + + ERROR = 0x100, R_ERROR + ERROR_DISABLE = 0x104, R_ERROR + + # Found via register bruteforcing + STREAM_UNK_SET = irange(0x120, 8, 4), Register32 + STREAM_UNK_CLR = irange(0x140, 8, 4), Register32 + + # these are all accessed by error interrupt handler + REG_0x160 = 0x160, Register32 + REG_0x164 = 0x164, Register32 + ERROR_ADDR_LO = 0x170, Register32 + ERROR_ADDR_HI = 0x174, Register32 + REG_0x178 = 0x178, Register32 # hwrev 2 only + REG_0x180 = irange(0x180, 4, 4), Register32 + REG_0x1a0 = irange(0x1a0, 8, 4), Register32 + ERR_SECONDARY = irange(0x1c0, 8, 4), Register32 + + # 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. + REG_PROTECT = 0x200, R_PROTECT + REG_UNPROTECT = 0x204, R_PROTECT + REG_PROTECT_LOCK = 0x208, R_PROTECT + + # Tunables touch this, can set bits FF00001F, RW + REG_0x20c = 0x20c, Register32 + + DIAG_LOCK = 0x210, R_DIAG_LOCK + + # All unknown, related to transaction queueing??? + + # can set bits 3FFFFFFC, RW + REG_0x218 = 0x218, Register32 + # Tunables touch this, can set bits 000F0F0F, RW + REG_0x220 = 0x220, Register32 + # Tunables touch this, can set bits 00FFFFFF, RW + REG_0x224 = 0x224, Register32 + # can set bits 3F3F3F3F + TLIMIT = 0x228, Register32 + # can set bits 07070707 + TEQRESERVE = 0x22c, Register32 + # RO, outstanding transaction count??? + TRANS = irange(0x230, 4, 4), Register32 + + # hwrev 2 only for all of these + REG_0x300 = 0x300, Register32 + REG_0x308 = 0x308, Register32 + REG_0x310 = 0x310, Register32 + REG_0x318 = 0x318, Register32 + REG_0x320 = 0x320, Register32 + REG_0x328 = 0x328, Register32 + REG_0x330 = 0x330, Register32 + REG_0x338 = 0x338, Register32 + REG_0x340 = 0x340, Register32 + REG_0x348 = 0x348, Register32 + REG_0x350 = 0x350, Register32 + REG_0x358 = 0x358, Register32 + + # Unknown + REG_0x400 = 0x400, Register32 # can set 00000003 + REG_0x404 = 0x404, Register32 # can set 001FFFFF + REG_0x408 = 0x408, Register32 # can set 00FFFFFC + REG_0x410 = 0x410, Register32 # can set 3FFFFFFC + + # These registers exist even though it's "not supported" + TZ_CONFIG = 0x500, Register32 # 3 bits + TZ_SELECT = 0x504, Register32 # 1 bit + TZ_REGION0_START = 0x508, Register32 + TZ_REGION0_END = 0x510, Register32 + TZ_REGION0_OFFSET = 0x518, Register32 + TZ_REGION1_START = 0x520, Register32 + TZ_REGION1_END = 0x528, Register32 + TZ_REGION1_OFFSET = 0x530, Register32 + TZ_REGION2_START = 0x538, Register32 + TZ_REGION2_END = 0x540, Register32 + TZ_REGION2_OFFSET = 0x548, Register32 + + # completely guessed, unverified, can set bits 0F077077 + PERF_INTR_ENABLE = 0x700, Register32 + PERF_INTR_STATUS = 0x704, Register32 + + PERF_UNK1 = irange(0x720, 8, 4), Register32 + PERF_UNK2 = irange(0x740, 8, 4), Register32 + + PERF_TLB_MISS = 0x760, Register32 + PERF_TLB_FILL = 0x764, Register32 + PERF_TLB_HIT = 0x768, Register32 + PERF_ST_MISS = 0x770, Register32 + PERF_ST_FILL = 0x774, Register32 + PERF_ST_HIT = 0x778, Register32 + # hwrev 1 doesn't have these + PERF_CTC_MISS = 0x780, Register32 + PERF_CTC_FILL = 0x784, Register32 + PERF_CTC_HIT = 0x788, Register32 + + UNK_TUNABLES = irange(0x800, 256, 4), Register32 + + ENABLE_STREAMS = irange(0xc00, 8, 4), Register32 + DISABLE_STREAMS = irange(0xc20, 8, 4), Register32 + + TCR = irange(0x1000, 256, 4), R_TCR + TTBR = irange(0x1400, 256, 4), R_TTBR + + +class DART8110(Reloadable): + PAGE_BITS = 14 + PAGE_SIZE = 1 << PAGE_BITS + + 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): + self.iface = iface + self.regs = regs + self.u = util + self.pt_cache = {} + + enabled_streams = 0 + for i in range(8): + enabled_streams |= regs.ENABLE_STREAMS[i].val << 32*i + self.enabled_streams = enabled_streams + + @classmethod + def from_adt(cls, u, path, instance=0, **kwargs): + dart_addr = u.adt[path].get_reg(instance)[0] + regs = DART8110Regs(u, dart_addr) + dart = cls(u.iface, regs, u, **kwargs) + 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.ENABLE_STREAMS[stream // 32].val |= (1 << (stream % 32)) + + 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 + + ttbr = self.regs.TTBR[stream].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 >> self.PAGE_BITS + self.regs.TTBR[stream].reg = ttbr + + cached, l1 = self.get_pt(ttbr.ADDR << self.PAGE_BITS) + l1idx = (page >> self.L1_OFF) & self.IDX_MASK + l1pte = PTE(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 = PTE( + OFFSET=l2addr >> self.PAGE_BITS, VALID=1) + l1[l1idx] = l1pte.value + dirty.add(ttbr.ADDR << self.PAGE_BITS) + 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] = PTE( + SP_START=0, SP_END=0xfff, + OFFSET=paddr >> self.PAGE_BITS, VALID=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: + # FIXME this may not be correct + return [(start, size)] + + if tcr.BYPASS_DART or not tcr.TRANSLATE_ENABLE: + raise Exception(f"Unknown DART mode {tcr}") + + start = start & 0xfffffffff + + 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): + ttbr = self.regs.TTBR[stream].reg + if not ttbr.VALID: + pages.append(None) + continue + + cached, l1 = self.get_pt(ttbr.ADDR << self.PAGE_BITS) + l1pte = PTE(l1[(page >> self.L1_OFF) & self.IDX_MASK]) + if not l1pte.VALID and cached: + cached, l1 = self.get_pt(ttbr.ADDR << self.PAGE_BITS, uncached=True) + l1pte = PTE(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 = PTE(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 = PTE(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): + self.regs.TTBR[i].reg = R_TTBR(VALID = 0) + + # self.regs.ERROR.val = 0xffffffff + # self.regs.UNK1.val = 0 + self.regs.DISABLE_STREAMS[0].val = 0xffff + 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 = 0x80000004 + + def invalidate_streams(self, streams=0xffff): + for sid in range(256): + if streams & (1 << sid): + self.regs.TLB_OP.val = R_TLB_OP(STREAM=sid, OP=1) + while self.regs.TLB_OP.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): %09x ... %09x -> %016x [%d%d%d%d]" % ( + start, base + start*0x4000, base + (start+1)*0x4000, + pte.OFFSET << self.PAGE_BITS, + pte.RDPROT, pte.WRPROT, pte.UNCACHABLE, pte.VALID)) + if start < last: + print(" ==> (%4d): ... %09x -> %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 = PTE(VALID=0) + + for i, pte in enumerate(tbl): + pte = PTE(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 = PTE(pte) + if not pte.VALID: + if not unmapped: + print(" ...") + unmapped = True + continue + + unmapped = False + + print(" table (%d): %09x ... %09x -> %016x [%d%d%d%d]" % ( + i, base + i*0x2000000, base + (i+1)*0x2000000, + pte.OFFSET << self.PAGE_BITS, + pte.RDPROT, pte.WRPROT, pte.UNCACHABLE, pte.VALID)) + self.dump_table2(base + i*0x2000000, pte.OFFSET << self.PAGE_BITS) + + def dump_ttbr(self, ttbr): + if not ttbr.VALID: + return + + l1_addr = (ttbr.ADDR) << self.PAGE_BITS + print(" TTBR: %011x" % (l1_addr)) + + self.dump_table(0, l1_addr) + + def dump_device(self, idx): + tcr = self.regs.TCR[idx].reg + ttbr = self.regs.TTBR[idx] + print(f"dev {idx:02x}: TCR={tcr!s} TTBR = {ttbr!s}") + + if tcr.TRANSLATE_ENABLE and tcr.BYPASS_DART: + print(" mode: INVALID") + elif tcr.TRANSLATE_ENABLE: + print(" mode: TRANSLATE") + + self.dump_ttbr(ttbr.reg) + elif tcr.BYPASS_DART: + print(" mode: BYPASS") + else: + print(" mode: UNKNOWN") + + def dump_params(self): + print(self.regs.PARAMS_0.reg) + print(self.regs.PARAMS_4.reg) + print(self.regs.PARAMS_8.reg) + print(self.regs.PARAMS_C.reg) diff --git a/tools/proxyclient/m1n1/hw/dockchannel.py b/tools/proxyclient/m1n1/hw/dockchannel.py new file mode 100644 index 0000000..1a9443e --- /dev/null +++ b/tools/proxyclient/m1n1/hw/dockchannel.py @@ -0,0 +1,120 @@ +# SPDX-License-Identifier: MIT +import struct + +from ..utils import * + +__all__ = ["DockChannel"] + +# DockChannel layout: +# 00000 : Global regs + +# 08000 : IRQ regs (0) +# 0c000 : IRQ regs (1) +# 10000 : IRQ regs (2) +# 14000 : IRQ regs (3) -> AIC #0 +# 18000 : IRQ regs (4) -> AIC #1 +# 1c000 : IRQ regs (5) (not always present) + +# 28000 : FIFO regs (1A) +# 2c000 : Data regs (1A) +# 30000 : FIFO regs (1B) +# 34000 : Data regs (1B) +# 38000 : FIFO regs (2A) +# 3c000 : Data regs (2A) +# 40000 : FIFO regs (2B) +# 44000 : Data regs (2B) +# (possibly more) + +class R_RX_DATA(Register32): + DATA = 31, 8 + COUNT = 7, 0 + +class DockChannelIRQRegs(RegMap): + IRQ_MASK = 0x0, Register32 + IRQ_FLAG = 0x4, Register32 + +class DockChannelConfigRegs(RegMap): + TX_THRESH = 0x0, Register32 + RX_THRESH = 0x4, Register32 + +class DockChannelDataRegs(RegMap): + TX_8 = 0x4, Register32 + TX_16 = 0x8, Register32 + TX_24 = 0xc, Register32 + TX_32 = 0x10, Register32 + TX_FREE = 0x14, Register32 + RX_8 = 0x1c, R_RX_DATA + RX_16 = 0x20, R_RX_DATA + RX_24 = 0x24, R_RX_DATA + RX_32 = 0x28, Register32 + RX_COUNT = 0x2c, Register32 + +class DockChannel: + def __init__(self, u, irq_base, fifo_base, irq_idx): + self.u = u + self.p = u.proxy + self.iface = u.iface + self.config = DockChannelConfigRegs(u, fifo_base) + self.data = DockChannelDataRegs(u, fifo_base + 0x4000) + self.irq = DockChannelIRQRegs(u, irq_base) + self.irq_idx = irq_idx + self.irq.IRQ_MASK.val = 3 << (irq_idx * 2) + + @property + def tx_irq(self): + self.irq.IRQ_FLAG.val = 1 << (self.irq_idx * 2) + return self.irq.IRQ_FLAG.val & (1 << (self.irq_idx * 2)) + + @property + def rx_irq(self): + self.irq.IRQ_FLAG.val = 2 << (self.irq_idx * 2) + return self.irq.IRQ_FLAG.val & (2 << (self.irq_idx * 2)) + + @property + def rx_count(self): + return self.data.RX_COUNT.val + + @property + def tx_free(self): + return self.data.TX_FREE.val + + def set_tx_thresh(self, v): + self.config.TX_THRESH.val = v + + def set_rx_thresh(self, v): + self.config.RX_THRESH.val = v + + def write(self, data): + p = 0 + left = len(data) + while left >= 4: + while self.tx_free < 4: + pass + d = struct.unpack("<I", data[p:p+4])[0] + self.data.TX_32.val = d + p += 4 + left -= 4 + while left >= 1: + while self.tx_free < 1: + pass + self.data.TX_8.val = data[p] + p += 1 + left -= 1 + + def read(self, count): + data = [] + left = count + while left >= 4: + while self.rx_count < 4: + pass + data.append(struct.pack("<I", self.data.RX_32.val)) + left -= 4 + while left >= 1: + while self.rx_count < 1: + pass + data.append(bytes([self.data.RX_8.DATA])) + left -= 1 + return b"".join(data) + + def read_all(self): + return self.read(self.rx_count) diff --git a/tools/proxyclient/m1n1/hw/dwc3.py b/tools/proxyclient/m1n1/hw/dwc3.py new file mode 100644 index 0000000..41948f4 --- /dev/null +++ b/tools/proxyclient/m1n1/hw/dwc3.py @@ -0,0 +1,268 @@ +# SPDX-License-Identifier: MIT + +from enum import IntEnum +from m1n1.utils import * + + +class R_XHCI_USBCMD(Register32): + EU3S = 11 + EWE = 10 + CRS = 9 + CSS = 8 + LHCRST = 7 + HSEE = 3 + INTE = 2 + HCRST = 1 + RS = 0 + + +class R_XHCI_USBSTS(Register32): + HCE = 12 + CNR = 11 + SRE = 10 + RSS = 9 + SSS = 8 + PCD = 4 + EINT = 3 + HSE = 2 + HCH = 0 + + +class R_XHCI_CRCR_LO(Register32): + CRP = 31, 6 + CRR = 3 + CA = 2 + CS = 1 + RCS = 0 + + +class R_XHCI_DNCTRL(Register32): + N0_N15 = 15, 0 + + +class R_XHCI_DOORBELL(Register32): + TASK_ID = 31, 16 + RSVD = 15, 8 + TARGET = 7, 0 + + +class R_XHCI_PORTSC(Register32): + CCS = 0 + PED = 1 + OCA = 3 + RESET = 4 + PLS = 8, 5 + PP = 9 + SPEED = 13, 10 + PIC = 15, 14 + LWS = 16 + CSC = 17 + PEC = 18 + WRC = 19 + OCC = 20 + PRC = 21 + PLC = 22 + CEC = 23 + CAS = 24 + WCE = 25 + WDE = 26 + WOE = 27 + DR = 30 + WPR = 31 + + +class R_XHCI_PORTLI(Register32): + ERROR_CNT = 15, 0 + RLC = 19, 16 + TLC = 23, 20 + RSV = 31, 24 + + +class R_XHCI_IMAN(Register32): + IP = 0 + IE = 1 + + +class XhciRegs(RegMap): + HCSPARAMS1 = 0x04, Register32 + HCSPARAMS2 = 0x08, Register32 + HCSPARAMS3 = 0x0C, Register32 + HCCPARAMS1 = 0x10, Register32 + DBOFF = 0x14, Register32 + RTSOFF = 0x18, Register32 + HCCPARAMS2 = 0x1C, Register32 + USBCMD = 0x20, R_XHCI_USBCMD + USBSTS = 0x24, R_XHCI_USBSTS + DNCTRL = 0x34, R_XHCI_DNCTRL + CRCR_LO = 0x38, R_XHCI_CRCR_LO + CRCR_HI = 0x3C, Register32 + DCBAAP_LO = 0x50, Register32 + DCBAAP_HI = 0x54, Register32 + + PORTSC0 = 0x420, R_XHCI_PORTSC + PORTPMSC0 = 0x424, Register32 + PORTLI0 = 0x428, R_XHCI_PORTLI + PORTHLPMC0 = 0x42C, Register32 + + PORTSC1 = 0x430, R_XHCI_PORTSC + PORTPMSC1 = 0x434, Register32 + PORTLI1 = 0x438, R_XHCI_PORTLI + PORTHLPMC1 = 0x43C, Register32 + + MFINDEX = 0x440, Register32 + IMAN0 = 0x460 + 0x00, R_XHCI_IMAN + IMOD0 = 0x460 + 0x04, Register32 + ERSTSZ0 = 0x460 + 0x08, Register32 + RSVD0 = 0x460 + 0x0C, Register32 + ERSTBA0 = 0x460 + 0x10, Register64 + ERDP0 = 0x460 + 0x18, Register64 + + IMAN1 = 0x480 + 0x00, R_XHCI_IMAN + IMOD1 = 0x480 + 0x04, Register32 + ERSTSZ1 = 0x480 + 0x08, Register32 + RSVD1 = 0x480 + 0x0C, Register32 + ERSTBA1 = 0x480 + 0x10, Register64 + ERDP1 = 0x480 + 0x18, Register64 + + IMAN2 = 0x4A0 + 0x00, R_XHCI_IMAN + IMOD2 = 0x4A0 + 0x04, Register32 + ERSTSZ2 = 0x4A0 + 0x08, Register32 + RSVD0 = 0x4A0 + 0x0C, Register32 + ERSTBA2 = 0x4A0 + 0x10, Register64 + ERDP2 = 0x4A0 + 0x18, Register64 + + IMAN3 = 0x4C0 + 0x00, R_XHCI_IMAN + IMOD3 = 0x4C0 + 0x04, Register32 + ERSTSZ3 = 0x4C0 + 0x08, Register32 + RSVD0 = 0x4C0 + 0x0C, Register32 + ERSTBA3 = 0x4C0 + 0x10, Register64 + ERDP3 = 0x4C0 + 0x18, Register64 + + DOORBELL = irange(0x4E0, 256, 4), R_XHCI_DOORBELL + + +class R_GUSB3PIPECTL(Register32): + PHYSOFTRST = 31 + U2SSINP3OK = 29 + DISRXDETINP3 = 28 + UX_EXIT_PX = 27 + REQP1P2P3 = 24 + DEPOCHANGE = 18 + SUSPHY = 17 + LFPSFILT = 9 + RX_DETOPOLL = 8 + + +class R_GUSB2PHYCFG(Register32): + PHYSOFTRST = 31 + U2_FREECLK_EXISTS = 30 + SUSPHY = 6 + ULPI_UTMI = 4 + ENBLSLPM = 8 + + +class R_GCTL(Register32): + U2RSTECN = 16 + PRTCAP = 14, 12 + CORESOFTRESET = 11 + SOFITPSYNC = 10 + SCALEDOWN = 6, 4 + DISSCRAMBLE = 3 + U2EXIT_LFPS = 2 + GBLHIBERNATIONEN = 1 + DSBLCLKGTNG = 0 + + +class Dwc3CoreRegs(RegMap): + GSBUSCFG0 = 0x100, Register32 + GSBUSCFG1 = 0x104, Register32 + GTXTHRCFG = 0x108, Register32 + GRXTHRCFG = 0x10C, Register32 + GCTL = 0x110, R_GCTL + GEVTEN = 0x114, Register32 + GSTS = 0x118, Register32 + GUCTL1 = 0x11C, Register32 + GSNPSID = 0x120, Register32 + GGPIO = 0x124, Register32 + GUID = 0x128, Register32 + GUCTL = 0x12C, Register32 + GBUSERRADDR0 = 0x130, Register32 + GBUSERRADDR1 = 0x134, Register32 + GPRTBIMAP0 = 0x138, Register32 + GPRTBIMAP1 = 0x13C, Register32 + GHWPARAMS0 = 0x140, Register32 + GHWPARAMS1 = 0x144, Register32 + GHWPARAMS2 = 0x148, Register32 + GHWPARAMS3 = 0x14C, Register32 + GHWPARAMS4 = 0x150, Register32 + GHWPARAMS5 = 0x154, Register32 + GHWPARAMS6 = 0x158, Register32 + GHWPARAMS7 = 0x15C, Register32 + GDBGFIFOSPACE = 0x160, Register32 + GDBGLTSSM = 0x164, Register32 + GDBGBMU = 0x16C, Register32 + GDBGLSPMUX = 0x170, Register32 + GDBGLSP = 0x174, Register32 + GDBGEPINFO0 = 0x178, Register32 + GDBGEPINFO1 = 0x17C, Register32 + GPRTBIMAP_HS0 = 0x180, Register32 + GPRTBIMAP_HS1 = 0x184, Register32 + GPRTBIMAP_FS0 = 0x188, Register32 + GPRTBIMAP_FS1 = 0x18C, Register32 + GUCTL2 = 0x19C, Register32 + GUSB2PHYCFG = 0x200, R_GUSB2PHYCFG + GUSB2I2CCTL = 0x240, Register32 + GUSB2PHYACC = 0x280, Register32 + GUSB3PIPECTL = 0x2C0, R_GUSB3PIPECTL + DWC3_GHWPARAMS8 = 0x600, Register32 + DWC3_GUCTL3 = 0x60C, Register32 + DWC3_GFLADJ = 0x630, Register32 + DWC3_GHWPARAMS9 = 0x680, Register32 + + +class R_PIPEHANDLER_OVERRIDE(Register32): + RXVALID = 0 + RXDETECT = 2 + + +class E_PIPEHANDLER_MUX_MODE(IntEnum): + USB3_PHY = 0 + DUMMY_PHY = 1 + UNK2 = 2 + + +class E_PIPEHANDLER_CLK_SELECT(IntEnum): + UNK0 = 0 + USB3_PHY = 1 + DUMMY_PHY = 2 + UNK4 = 4 + + +class R_PIPEHANDLER_MUX_CTRL(Register32): + MUX_MODE = 1, 0, E_PIPEHANDLER_MUX_MODE + CLK_SELECT = 5, 3, E_PIPEHANDLER_CLK_SELECT + + +class R_PIPEHANDLER_LOCK(Register32): + LOCK_EN = 0 + + +class R_PIPEHANDLER_AON_GEN(Register32): + DWC3_FORCE_CLAMP_EN = 4 + DWC3_RESET_N = 0 + + +class R_PIPEHANDLER_NONSELECTED_OVERRIDE(Register32): + NATIVE_POWER_DOWN = 3, 0 + NATIVE_RESET = 12 + DUMMY_PHY_EN = 15 + + +class PipehandlerRegs(RegMap): + PIPEHANDLER_OVERRIDE = 0x00, R_PIPEHANDLER_OVERRIDE + PIPEHANDLER_OVERRIDE_VALUES = 0x04, R_PIPEHANDLER_OVERRIDE + PIPEHANDLER_MUX_CTRL = 0x0C, R_PIPEHANDLER_MUX_CTRL + PIPEHANDLER_LOCK_REQ = 0x10, R_PIPEHANDLER_LOCK + PIPEHANDLER_LOCK_ACK = 0x14, R_PIPEHANDLER_LOCK + PIPEHANDLER_AON_GEN = 0x1C, R_PIPEHANDLER_AON_GEN + PIPEHANDLER_NONSELECTED_OVERRIDE = 0x20, R_PIPEHANDLER_NONSELECTED_OVERRIDE diff --git a/tools/proxyclient/m1n1/hw/i2c.py b/tools/proxyclient/m1n1/hw/i2c.py new file mode 100644 index 0000000..e2bda7a --- /dev/null +++ b/tools/proxyclient/m1n1/hw/i2c.py @@ -0,0 +1,251 @@ +# SPDX-License-Identifier: MIT +from ..utils import * +from enum import IntEnum + +__all__ = ["I2C", "I2CRegs"] + + +class R_MTXFIFO(Register32): + READ = 10 # Read (DATA=count) + STOP = 9 # Issue START before + START = 8 # Issue STOP after + DATA = 7, 0 # Byte to send or count + +class R_MRXFIFO(Register32): + EMPTY = 8 # FIFO empty + DATA = 7, 0 # FIFO data + +class R_MCNT(Register32): + S_RXCNT = 31, 24 # Slave RX count + S_TXCNT = 23, 16 # Slave TX count + M_RXCNT = 15, 8 # Master RX count + M_TXCNT = 7, 0 # Master TX count + +class E_MST(IntEnum): + IDLE = 0 + FRD1 = 1 + FRD2 = 2 + COMMAND = 3 + START = 4 + WRITE = 5 + READ = 6 + ACK = 7 + STOP = 8 + BAD = 15 + +class E_SST(IntEnum): + IDLE = 0 + START = 1 + ST_ACK = 2 + DATA = 3 + ACK = 4 + +class R_XFSTA(Register32): + MST = 27, 24, E_MST # Master controller state + SRD = 20 # Slave read in progress + SWR = 19 # Slave write in progress + SST = 18, 16, E_SST # Slave controller state + XFIFO = 9, 8 # FIFO number for error + XFCNT = 7, 0 # Number of bytes in current xfer + +class R_SADDR(Register32): + DEB = 31 # Enable SDA/SCL read debug + DIR = 30 # Direct (bitbang) mode + ENS = 29 # Enable slave interface + RST_STX = 28 # Reset slave TX FIFO + RST_SRX = 27 # Reset master RX fifo (if ^ both, controller too) + PEN = 26 # Promiscuous mode (slave) + AAE = 25 # SALT/ALTMASK enable + SAE = 24 # SADDR enable + ALTMASK = 23, 16 # MASK for SALT bits + SALT = 15, 8 # Alt slave address + SADDR = 7, 0 # Slave address + +class R_SMSTA(Register32): + XIP = 28 # Xaction in progress + XEN = 27 # Xaction ended + UJF = 26 # UnJam failure + JMD = 25 # Jam ocurred + JAM = 24 # Currently jammed + MTO = 23 # Master timeout + MTA = 22 # Master arb lost + MTN = 21 # Master received NACK + MRF = 20 # Master RX fifo full + MRNE = 19 # Master RX fifo not empty + MTF = 17 # Master TX fifo full + MTE = 16 # Master RX fifo empty + STO = 15 # Slave timeout + STA = 14 # Slave arb lost + STN = 13 # Slave received NACK + SRF = 12 # Slave RX fifo full + SRNE = 11 # Slave RX fifo not empty + STR = 10 # Slave transmit required + STF = 9 # Slave TX fifo full + STE = 8 # Slave TX fifo empty + TOS = 7 # Timeout due to slave FIFO + TOM = 6 # Timeout due to master FIFO + TOE = 5 # Slave timeout due to ext clock stretch + DCI = 4 # Direct clock in + DDI = 3 # Direct data in + DCO = 2 # Direct clock out + DDO = 1 # Direct data out + NN = 0 # NACK next (slave) + +class R_CTL(Register32): + MSW = 26, 16 # Maximum slave write size + ENABLE = 11 # Unknown enable bit (clock sel? Apple thing) + MRR = 10 # Master receive FIFO reset + MTR = 9 # Master transmit FIFO reset + UJM = 8 # Enable auto unjam machine + CLK = 7, 0 # Clock divider + +class R_STXFIFO(Register32): + DATA = 7, 0 # Data + +class R_SRXFIFO(Register32): + N = 12 # NACK received after this byte + P = 11 # Stop received, data not valid + S = 10 # Start received before + O = 9 # Overflow (promisc only) + E = 8 # Empty (data not valid) + DATA = 7, 0 # Data + +# Apple reg +class R_FIFOCTL(Register32): + HALT = 0 # Halt machinery + +class I2CRegs(RegMap): + MTXFIFO = 0x00, R_MTXFIFO + MRXFIFO = 0x04, R_MRXFIFO + MCNT = 0x08, R_MCNT + XFSTA = 0x0c, R_XFSTA + SADDR = 0x10, R_SADDR + SMSTA = 0x14, R_SMSTA + IMASK = 0x18, R_SMSTA + CTL = 0x1c, R_CTL + STXFIFO = 0x20, R_STXFIFO + SRXFIFO = 0x20, R_SRXFIFO + FIFOCTL = 0x44, R_FIFOCTL + + +class I2C: + def __init__(self, u, adt_path): + self.u = u + self.p = u.proxy + self.iface = u.iface + self.base = u.adt[adt_path].get_reg(0)[0] + self.regs = I2CRegs(u, self.base) + self.devs = [] + + def clear_fifos(self): + self.regs.CTL.set(MTR=1, MRR=1) + + def clear_status(self): + self.regs.SMSTA.val = 0xffffffff + + def _fifo_read(self, nbytes): + read = [] + for _ in range(nbytes): + val = self.regs.MRXFIFO.reg + timeout = 10000 + while val.EMPTY and timeout > 0: + val = self.regs.MRXFIFO.reg + timeout -= 1 + if timeout == 0: + raise Exception("timeout") + read.append(int(val) & 0xff) + return bytes(read) + + def _fifo_write(self, buf, stop=False): + for no, byte in enumerate(buf): + sending_stop = stop and no == len(buf) - 1 + self.regs.MTXFIFO.set(DATA=byte, STOP=int(sending_stop)) + + if not stop: + return + + timeout = 10000 + while not self.regs.SMSTA.reg.XEN and timeout > 0: + timeout -= 1 + if timeout == 0: + raise Exception("timeout") + + def write_reg(self, addr, reg, data, regaddrlen=1): + self.clear_fifos() + self.clear_status() + + self.regs.CTL.set(ENABLE=1, CLK=0x4) + self.regs.MTXFIFO.set(DATA=addr << 1, START=1) + regbytes = int.to_bytes(reg, regaddrlen, byteorder="big") + self._fifo_write(regbytes + bytes(data), stop=True) + self.regs.CTL.set(ENABLE=0, CLK=0x4) + + def read_reg(self, addr, reg, nbytes, regaddrlen=1): + self.clear_fifos() + self.clear_status() + + self.regs.CTL.set(ENABLE=1, CLK=0x4) + self.regs.MTXFIFO.set(DATA=addr << 1, START=1) + regbytes = int.to_bytes(reg, regaddrlen, byteorder="big") + self._fifo_write(regbytes, stop=False) + self.regs.MTXFIFO.set(DATA=(addr << 1) | 1, START=1) + self.regs.MTXFIFO.set(DATA=nbytes, STOP=1, READ=1) + data = self._fifo_read(nbytes) + self.regs.CTL.set(ENABLE=0, CLK=0x4) + return data + +class I2CRegMapDev: + REGMAP = None + ADDRESSING = (0, 1) + + def __init__(self, bus, addr, name=None): + self.bus = bus + self.addr = addr + self.curr_page = None + self.name = name + + self.paged, self.regimmbytes = self.ADDRESSING + if self.REGMAP is not None: + self.regs = self.REGMAP(self, 0) + + @classmethod + def from_adt(cls, bus, path): + node = bus.u.adt[path] + addr = node.reg[0] & 0xff + return cls(bus, addr, node.name) + + def _switch_page(self, page): + assert self.paged + self.bus.write_reg(self.addr, 0, bytes([page]), + regaddrlen=self.regimmbytes) + self.curr_page = page + + def _snip_regaddr(self, addr): + pageshift = self.regimmbytes * 8 + page = addr >> pageshift + immediate = addr & ~(~0 << pageshift) + return (page, immediate) + + def write(self, reg, val, width=8): + page, imm = self._snip_regaddr(reg) + + if self.paged and page != self.curr_page: + self._switch_page(page) + + valbytes = val.to_bytes(width//8, byteorder="little") + self.bus.write_reg(self.addr, imm, valbytes, + regaddrlen=self.regimmbytes) + + def read(self, reg, width=8): + page, imm = self._snip_regaddr(reg) + + if self.paged and page != self.curr_page: + self._switch_page(page) + + data = self.bus.read_reg(self.addr, imm, width//8, + regaddrlen=self.regimmbytes) + return int.from_bytes(data, byteorder='little') + + def __repr__(self): + label = self.name or f"@ {self.addr:02x}" + return f"<{type(self).__name__} {label}>" diff --git a/tools/proxyclient/m1n1/hw/isp.py b/tools/proxyclient/m1n1/hw/isp.py new file mode 100644 index 0000000..4d8d0fd --- /dev/null +++ b/tools/proxyclient/m1n1/hw/isp.py @@ -0,0 +1,507 @@ +import struct +from enum import IntEnum +from ..utils import * + +class ISPCommandDirection(IntEnum): + RX = 0 + TX = 1 + +class ISPCommand: + """ Represents a command in any IPC channel """ + + def __init__(self, channel, message, direction): + value, u0, u1 = struct.unpack('<3q40x', message.data) + self.message = message + self.channel = channel + self.direction = direction + self.tracer = channel.tracer + self.raw_value = value + self.value = value & 0xFFFFFFFFFFFFFFFC + self.arg0 = u0 + self.arg1 = u1 + + def dump(self): + self.log(f"[CMD Value: {hex(self.value)}, U0: {hex(self.arg0)}, U1: {hex(self.arg1)}]") + + def read_iova(self, address, length): + return self.tracer.dart.ioread(0, address, length) + + def valid(self): + return True + + def log(self, message): + if self.direction is ISPCommandDirection.RX: + self.tracer.log(f"<== [{self.channel.name}]({self.message.index}): {message}") + else: + self.tracer.log(f"==> [{self.channel.name}]({self.message.index}): {message}") + +class ISPTerminalCommand(ISPCommand): + """ Represents a command in TERMINAL channel + + A command arguments include a pointer to a buffer that contains log line + and the length of the buffer. Buffers are 0x80 bytes wide. + """ + # ISP sends buffer address at beginning + BUFFER_ADDRESS = None + # It seems messages are capped to 100 bytes + MAX_BUFFER_SIZE = 0x80 + + @staticmethod + def set_address(address): + if address != 0: + ISPTerminalCommand.BUFFER_ADDRESS = address + + @staticmethod + def move_cursor(): + if ISPTerminalCommand.BUFFER_ADDRESS: + ISPTerminalCommand.BUFFER_ADDRESS += ISPTerminalCommand.MAX_BUFFER_SIZE + else: + return None + + def __init__(self, channel, message, direction): + super().__init__(channel, message, direction) + + ## Set buffer address + ISPTerminalCommand.set_address(self.value) + + ## Read contents + self.buffer_message = self.read_iova(ISPTerminalCommand.BUFFER_ADDRESS, self.arg0) + + ## Move cursor + ISPTerminalCommand.move_cursor() + + def dump(self): + self.log(f"ISPCPU: {self.buffer_message}]") + + def log(self, message): + self.tracer.log(f"[{self.channel.name}]({str(self.message.index).ljust(3)}): {message}") + +class ISPIOCommand(ISPCommand): + """ Represents a command in IO channel + + An IO command is used to request ISP to perform some operations. The command + contains a pointer to a command struct which contains a OPCODE. The OPCODE + is used to differentate commands. + """ + + def __init__(self, channel, message, direction): + super().__init__(channel, message, direction) + self.iova = self.value + if self.iova != 0: + contents = self.read_iova(self.iova, 0x8) + self.contents = int.from_bytes(contents, byteorder="little") + else: + self.contents = None + + def dump(self): + if self.iova != 0: + self.log(f"[IO Addr: {hex(self.iova)}, Size: {hex(self.arg0)}, U1: {hex(self.arg1)} -> Opcode: {hex(self.contents >> 32)}]") + +class ISPT2HBufferCommand(ISPCommand): + """ Represents a command in BUF_T2H channel """ + def __init__(self, channel, message, direction): + super().__init__(channel, message, direction) + self.iova = self.value + if self.iova != 0: + self.contents = self.read_iova(self.iova, 0x280) + + def dump(self): + super().dump() + if self.iova != 0: + chexdump(self.contents) + +class ISPH2TBufferCommand(ISPCommand): + """ Represents a command in BUF_H2T channel """ + def __init__(self, channel, message, direction): + super().__init__(channel, message, direction) + self.iova = self.value + if self.iova != 0: + # Dumping first 0x20 bytes after iova translation, but no idea how internal struct + self.contents = self.read_iova(self.iova, 0x20) + + def dump(self): + super().dump() + if self.iova != 0: + chexdump(self.contents) + +class ISPT2HIOCommand(ISPCommand): + """ Represents a command in IO_T2H channel """ + def __init__(self, channel, message, direction): + super().__init__(channel, message, direction) + self.iova = self.value + if self.iova != 0: + # Dumping first 0x20 bytes after iova translation, but no idea how internal struct + self.contents = self.read_iova(self.iova, 0x20) + + def dump(self): + super().dump() + if self.iova != 0: + chexdump(self.contents) + +class ISPSharedMallocCommand(ISPCommand): + """ Represents a command in SHAREDMALLOC channel + + A command of this type can either request memory allocation or memory free + depending the arguments. When ISP needs to allocate memory, it puts a + message in the SHAREDMALLOC channel, message arguments are length of buffer + and type of allocation. + + CPU detects the new message, perform memory allocation and mutate the + original message to indicate the address of the allocated memory block. + """ + + def __init__(self, channel, message, direction): + super().__init__(channel, message, direction) + self.address = self.value + self.size = self.arg0 + self.type = self.arg1 #.to_bytes(8, byteorder="little") + + def dump(self): + if self.direction == ISPCommandDirection.RX: + if self.address is 0: + self.log(f"[FW Malloc, Length: {hex(self.size)}, Type: {hex(self.type)}]") + else: + self.log(f"[FW Free, Address: {hex(self.value)}, Length: {hex(self.size)}, Type: {hex(self.type)})]") + else: + if self.address is 0: + self.log(f"[FW Free]") + else: + self.log(f"[FW Malloc, Address: {hex(self.value)}, Type: {hex(self.type)})]") + +class ISPChannelTable: + """ A class used to present IPC table. + + The Channel Table describes the IPC channels available to communicate with + the ISP. + + In the M1 processor (tonga), the list of channels exposed by ISP are: + [CH - TERMINAL] (src = 0, type = 2, entries = 768, iova = 0x1804700) + [CH - IO] (src = 1, type = 0, entries = 8, iova = 0x1810700) + [CH - BUF_H2T] (src = 2, type = 0, entries = 64, iova = 0x1810b00) + [CH - BUF_T2H] (src = 3, type = 1, entries = 64, iova = 0x1811b00) + [CH - SHAREDMALLOC] (src = 3, type = 1, entries = 8, iova = 0x1812b00) + [CH - IO_T2H] (src = 3, type = 1, entries = 8, iova = 0x1812d00) + + Each entry in the table is 256 bytes wide. Here is the layout of each entry: + 0x00 - 0x1F = Name (NULL terminated string) + 0x20 - 0x3F = Padding + 0x40 - 0x43 = Type (DWORD) + 0x44 - 0x47 = Source (DWORD) + 0x48 - 0x4F = Entries (QWORD) + 0x50 - 0x58 = Address (QWORD) + """ + + ENTRY_LENGTH = 256 + + def __init__(self, tracer, number_of_channels, table_address): + self.tracer = tracer + self.address = table_address + self.count = number_of_channels + self.size = number_of_channels * self.ENTRY_LENGTH + self.channels = [] + + _table = self.ioread(self.address & 0xFFFFFFFF, self.size) + for offset in range(0, self.size, self.ENTRY_LENGTH): + _entry = _table[offset: offset + self.ENTRY_LENGTH] + _name, _type, _source, _entries, _address = struct.unpack('<32s32x2I2q168x', _entry) + _channel = ISPChannel(self, _name, _type, _source, _entries, _address) + # We want to process terminal logs as fast as possible before they are processed by CPU + # So we use a special implementation for TERMINAL channel that fetches all logs + if _channel.name == "TERMINAL": + _channel = ISPTerminalChannel(self, _name, _type, _source, _entries, _address) + self.channels.append(_channel) + + def get_last_write_command(self, doorbell_value): + """ Gets last written message given a Doorbell value """ + if self.channels and len(self.channels) > 0: + names = [] + channel_cmds = [] + for channel in self.channels: + # We want to process terminal logs as fast as possible before they are processed by CPU + if (channel.doorbell == doorbell_value) or channel.name == "TERMINAL": + names.append(channel.name) + for cmd in channel.get_commands(ISPCommandDirection.TX): + channel_cmds.append(cmd) + + self.log(f"CHs: [{(','.join(names))}]") + for cmd in channel_cmds: + cmd.dump() + + def get_last_read_command(self, pending_irq): + """ Gets last read message given a IRQ value """ + cmds = [] + scanned_channels = [] + if self.channels and len(self.channels) > 0: + cidx = 0 + for channel in self.channels: + if (pending_irq >> channel.source & 1) != 0: + scanned_channels.append(channel.name) + for cmd in channel.get_commands(ISPCommandDirection.RX): + cmds.append(cmd) + cidx = cidx + 1 + + if len(scanned_channels) > 0: + self.log(f"CHs: [{(','.join(scanned_channels))}]") + for cmd in cmds: + cmd.dump() + + def dump(self): + """ Dumps the content of each channel """ + if self.channels and len(self.channels) > 0: + for channel in self.channels: + channel.dump() + + def ioread(self, address, size): + return self.tracer.ioread(address, size) + + def log(self, message): + self.tracer.log(message) + + def __str__(self): + s = "======== CHANNEL TABLE ========\n" + for channel in self.channels: + s += f"\t{str(channel)}\n" + return s + +class ISPChannel: + """ A class used to represent IPC channel + + ISP channels are ring buffers used by communication between CPU and ISP. + channel length is measured in number of entries, each entry is 64 bytes, + so channel size is '(entries * 64)' bytes. + + Channel Source is used to filter out channels when processing interrupts + and doorbell. Each time CPU wants to notify ISP about a new message it + writes doorbell register. In the other hand, when ISP wants to notify CPU + about a new message it triggers a hardware interrupt. + + Channel Type is a mistery, but it seems to have a connection with cmd bit + mask. + """ + + ENTRY_LENGTH = 64 + + def __init__(self, table, name, _type, source, number_of_entries, address): + self.table = table + self.tracer = table.tracer + self.name = str(name, "ascii").rstrip('\x00') + self.source = source + self.type = _type + self.number_of_entries = number_of_entries + self.entry_size = self.ENTRY_LENGTH + self.size = self.number_of_entries * self.entry_size + self.address = address + self.doorbell = 1 << source + self.last_message_sent = None + self.last_message_received = None + + def get_commands(self, direction): + """ Gets a command from the channel""" + commands = [] + message = self.get_message(direction) + if message: + command = self.__convert2command__(message, direction) + if command: + commands.append(command) + return commands + + def get_message(self, direction): + """ Gets a message from the channel and increase the associated index """ + last_message = self.last_message_sent if direction is ISPCommandDirection.TX else self.last_message_received + index = (last_message.index + 1) if last_message else 0 + new_index, message = self.__read_message__(index) + if message: + if last_message and last_message == message: + return + + last_message = message + if direction is ISPCommandDirection.TX: + self.last_message_sent = last_message + else: + self.last_message_received = last_message + return message + + def dump(self): + """ Dumps the content of the channel """ + s = f"[{self.name}] Channel messages: \n" + for index in range(self.number_of_entries): + _, message = self.__read_message__(index) + s = s + "\t" + str(message) + "\n" + self.table.log(s) + + def __convert2command__(self, message, direction): + """ Converts a channel message into a command """ + if self.name == "TERMINAL": + return ISPTerminalCommand(self, message, direction) + elif self.name == "IO" or self.name == "DEBUG": + return ISPIOCommand(self, message, direction) + elif self.name == "SHAREDMALLOC": + return ISPSharedMallocCommand(self, message, direction) + elif self.name == "BUF_T2H": + return ISPT2HBufferCommand(self, message, direction) + elif self.name == "BUF_H2T": + return ISPH2TBufferCommand(self, message, direction) + elif self.name == "IO_T2H": + return ISPT2HIOCommand(self, message, direction) + else: + return ISPCommand(self, message, direction) + + def __read_message__(self, index): + message_data = self.__read_by_index__(index) + message = ISPChannelMessage(index, message_data) + if message.valid(): + index += 1 + if index >= self.number_of_entries: + index = 0 + return index, message + return 0, None + + def __read_by_index__(self, index): + return self.table.ioread(self.address + (self.entry_size * index), self.entry_size) + + def __str__(self): + return f"[CH - {str(self.name)}] (src = {self.source!s}, type = {self.type!s}, size = {self.number_of_entries!s}, iova = {hex(self.address)!s})" + +class ISPTerminalChannel(ISPChannel): + """ Special channel implementation for TERMINAL channel + Addresses of log buffers are removed from memory after MacOS processes them, + hence we want to be a little bit ahead of MacOS and fetch all entries if + possible. + """ + + def __init__(self, table, name, _type, source, number_of_entries, address): + super().__init__(table, name, _type, source, number_of_entries, address) + self.last_index = 0 + + def get_commands(self, direction): + """ Gets a command from the channel""" + commands = [] + for i in range(self.number_of_entries): + index = (self.last_index + i) % self.number_of_entries + _, message = self.__read_message__(index) + if message and message.valid(): + command = self.__convert2command__(message, ISPCommandDirection.RX) + if command: + commands.append(command) + else: + self.last_index = index + break + return commands + +class ISPChannelMessage: + """ A class used to represent IPC channel message or entry + + Each entry is 64 bytes, however only 24 bytes seems to be used. These 24 + bytes are divided in three qwords (8-bytes). + """ + + def __init__(self, index, data): + self.index = index + self.data = data + idx = 0 + for arg in struct.unpack('<8q', self.data): + setattr(self, f"arg{idx}", arg) + idx += 1 + + def valid(self): + """ Checks if a message seems to be valid + + So far I have observed that invalid messages or empty slots + are usually marked as 0x1 (or 0x3 in case of TERMINAL msgs) + """ + return (self.arg0 is not 0x1) and (self.arg0 is not 0x3) + + def __str__(self): + s = "ISP Message: {" + idx = 0 + for arg in struct.unpack('<8q', self.data): + s = s + f"Arg{idx}: {hex(arg)}, " + idx = idx + 1 + s = s + "}" + return s + + def __eq__(self, other): + return self.data == other.data + +class ISP_REVISION(Register32): + REVISION = 15, 0 + +class ISP_PMU(Register32): + STATUS = 7, 0 + OTHER = 63, 8 + +class ISP_PMU_SPECIAL_STATUS(Register32): + STATUS = 7, 0 + OTHER = 63, 8 + +class ISPRegs(RegMap): + ISP_CPU_CONTROL = 0x0000, Register32 + ISP_CPU_STATUS = 0x0004, Register32 + ISP_REVISION = 0x1800000, ISP_REVISION + ISP_POWER_UNKNOWN = 0x20e0080, Register32 + ISP_IRQ_INTERRUPT = 0x2104000, Register32 + ISP_IRQ_INTERRUPT_2 = 0x2104004, Register32 + ISP_SENSOR_REF_CLOCK = irange(0x2104190, 3, 4), Register32 + ISP_GPR0 = 0x2104170, Register32 + ISP_GPR1 = 0x2104174, Register32 + ISP_GPR2 = 0x2104178, Register32 + ISP_GPR3 = 0x210417c, Register32 + ISP_GPR4 = 0x2104180, Register32 + ISP_GPR5 = 0x2104184, Register32 + ISP_GPR6 = 0x2104188, Register32 + ISP_GPR7 = 0x210418c, Register32 + + ISP_DOORBELL_RING0 = 0x21043f0, Register32 + ISP_IRQ_INTERRUPT_ACK = 0x21043fc, Register32 + + ISP_SMBUS_REG_MTXFIFO = irange(0x2110000, 4, 0x1000), Register32 + ISP_SMBUS_REG_MRXFIFO = irange(0x2110004, 4, 0x1000), Register32 + ISP_SMBUS_REG_UNK_1 = irange(0x2110008, 4, 0x1000), Register32 + ISP_SMBUS_REG_UNK_2 = irange(0x211000c, 4, 0x1000), Register32 + ISP_SMBUS_REG_UNK_3 = irange(0x2110010, 4, 0x1000), Register32 + ISP_SMBUS_REG_SMSTA = irange(0x2110014, 4, 0x1000), Register32 + ISP_SMBUS_REG_UNK_4 = irange(0x2110018, 4, 0x1000), Register32 + ISP_SMBUS_REG_CTL = irange(0x211001c, 4, 0x1000), Register32 + ISP_SMBUS_REG_UNK_5 = irange(0x2110020, 4, 0x1000), Register32 + ISP_SMBUS_REG_UNK_6 = irange(0x2110024, 4, 0x1000), Register32 + ISP_SMBUS_REG_REV = irange(0x2110028, 4, 0x1000), Register32 + ISP_SMBUS_REG_UNK_7 = irange(0x211002c, 4, 0x1000), Register32 + ISP_SMBUS_REG_UNK_8 = irange(0x2110030, 4, 0x1000), Register32 + ISP_SMBUS_REG_UNK_9 = irange(0x2110034, 4, 0x1000), Register32 + ISP_SMBUS_REG_UNK_A = irange(0x2110038, 4, 0x1000), Register32 + ISP_SMBUS_REG_UNK_B = irange(0x211003c, 4, 0x1000), Register32 + + ISP_DPE_REG_UNK1 = 0x2504000, Register32 + ISP_DPE_REG_UNK2 = 0x2508000, Register32 + + ISP_CPU_BUFFER = 0x1050000, Register32 + + ISP_SPMI0_REGISTER_BASE = 0x2900000, Register32 + ISP_SPMI1_REGISTER_BASE = 0x2920000, Register32 + ISP_SPMI2_REGISTER_BASE = 0x2940000, Register32 + +class PSReg(RegMap): + PMU_UNKNOWN0 = 0x4000, ISP_PMU + PMU_UNKNOWN1 = 0x4008, ISP_PMU + PMU_UNKNOWN2 = 0x4010, ISP_PMU + PMU_UNKNOWN3 = 0x4018, ISP_PMU + PMU_UNKNOWN4 = 0x4020, ISP_PMU + PMU_UNKNOWN5 = 0x4028, ISP_PMU + PMU_UNKNOWN6 = 0x4030, ISP_PMU + PMU_UNKNOWN7 = 0x4038, ISP_PMU + PMU_UNKNOWN8 = 0x4040, ISP_PMU + PMU_UNKNOWN9 = 0x4048, ISP_PMU + PMU_UNKNOWNA = 0x4050, ISP_PMU + PMU_UNKNOWNB = 0x4058, ISP_PMU + PMU_SPECIAL_STATUS = 0x4060, ISP_PMU_SPECIAL_STATUS + CLOCK_TICK_LOW = 0x34004, Register32 + CLOCK_TICK_HIGH = 0x34008, Register32 + RT_BANDWIDTH_SCRATCH1 = 0x38014, Register32 + RT_BANDWIDTH_SCRATCH2 = 0x38018, Register32 + +class SPMIReg(RegMap): + SPMI_UNKNOWN0 = 0x28, Register32 + SPMI_UNKNOWN1 = 0x40, Register32 + SPMI_UNKNOWN2 = 0x90, Register32 + SPMI_UNKNOWN3 = 0x80a0, Register32 + SPMI_UNKNOWN4 = 0x80a4, Register32
\ No newline at end of file diff --git a/tools/proxyclient/m1n1/hw/jpeg.py b/tools/proxyclient/m1n1/hw/jpeg.py new file mode 100644 index 0000000..c92bfe0 --- /dev/null +++ b/tools/proxyclient/m1n1/hw/jpeg.py @@ -0,0 +1,334 @@ +# SPDX-License-Identifier: MIT +from ..utils import * +from enum import IntEnum + + +class R_STATUS(Register32): + DONE = 0 + TIMEOUT = 1 + RD_BUF_OVERFLOW = 2 + WR_BUF_OVERFLOW = 3 + CODEC_BUF_OVERFLOW = 4 + SOME_KIND_OF_MACROBLOCK_SIZE_ERROR = 5 + AXI_ERROR = 6 + UNKNOWN_FLAG = 7 + + +class E_CODEC(IntEnum): + _444 = 0 + _422 = 1 + _411 = 2 + _420 = 3 + _400 = 4 + + +class R_CODEC(Register32): + CODEC = 2, 0, E_CODEC + + +class E_ENCODE_PIXEL_FORMAT(IntEnum): + RGB101010 = 0 + YUV10_linear = 1 + RGB888 = 2 + RGB565 = 3 + YUV_planar = 4 # partially tested, details not understood + YUV_linear = 5 # partially tested, details not understood + + +class R_ENCODE_PIXEL_FORMAT(Register32): + FORMAT = 4, 0, E_ENCODE_PIXEL_FORMAT + + +class E_SCALE(IntEnum): + DIV1 = 0 + DIV2 = 1 + DIV4 = 2 + DIV8 = 3 + + +class R_SCALE_FACTOR(Register32): + SCALE = 1, 0, E_SCALE + + +class E_DECODE_PIXEL_FORMAT(IntEnum): + YUV444_planar = 0 + YUV422_planar = 1 + YUV420_planar = 2 + YUV422_linear = 3 + _YUV10_broken_doesnt_work = 4 + RGBA8888 = 5 + RGB565 = 6 + _RGB101010_broken_doesnt_work = 7 + + +class R_DECODE_PIXEL_FORMAT(Register32): + FORMAT = 3, 0, E_DECODE_PIXEL_FORMAT + + +class E_JPEG_IO_FLAGS_SUBSAMPLING(IntEnum): + _444 = 0 + _422 = 1 + _420 = 2 + _400 = 3 + FOUR_COMPONENTS_MODE = 4 + _411_BROKEN = 6 + + +class R_JPEG_IO_FLAGS(Register32): + SUBSAMPLING_MODE = 2, 0, E_JPEG_IO_FLAGS_SUBSAMPLING + # not sure what this is supposed to do + MAKE_DECODE_WORK_BREAK_ENCODE = 3 + OUTPUT_MACROBLOCKS_UNFLIPPED_H = 4 + OUTPUT_8BYTE_CHUNKS_CORRECTLY = 5 + + +class R_JPEG_OUTPUT_FLAGS(Register32): + # bit0 doesn't seem to do anything + SKIP_HEADERS = 1 # output only SOS/EOI, no SOI/DQT/SOF0/DHT + OUTPUT_SOF0_AFTER_DHT = 2 # output SOF0 after DHT instead of before it + # bit3 doesn't seem to do anything + COMPRESS_WORSE = 4 # not sure exactly what this does + + +class R_QTBL_SEL(Register32): + COMPONENT0 = 1, 0 + COMPONENT1 = 3, 2 + COMPONENT2 = 5, 4 + COMPONENT3 = 7, 6 # guessed + + +class JPEGRegs(RegMap): + REG_0x0 = 0x0, Register32 + REG_0x4 = 0x4, Register32 + MODE = 0x8, Register32 + REG_0xc = 0xc, Register32 + + REG_0x10 = 0x10, Register32 + REG_0x14 = 0x14, Register32 + REG_0x18 = 0x18, Register32 + # REG_0x1c = 0x1c, Register32 + + REG_0x20 = 0x20, Register32 + STATUS = 0x24, R_STATUS + + CODEC = 0x28, R_CODEC + + REG_0x2c = 0x2c, Register32 + REG_0x30 = 0x30, Register32 + REG_0x34 = 0x34, Register32 + # this changes the output drastically if set to 1 for decode + # breaks encode if not set to 1 + REG_0x38 = 0x38, Register32 + + # not sure what the difference is. siting? type2 seems to win over type1 + CHROMA_HALVE_H_TYPE1 = 0x3c, Register32 + CHROMA_HALVE_H_TYPE2 = 0x40, Register32 + CHROMA_HALVE_V_TYPE1 = 0x44, Register32 + CHROMA_HALVE_V_TYPE2 = 0x48, Register32 + + # if double and quadruple both set --> double + CHROMA_DOUBLE_H = 0x4c, Register32 + CHROMA_QUADRUPLE_H = 0x50, Register32 + CHROMA_DOUBLE_V = 0x54, Register32 + + # details not fully understood yet + PX_USE_PLANE1 = 0x58, Register32 + PX_TILES_W = 0x5c, Register32 + PX_TILES_H = 0x60, Register32 + PX_PLANE0_WIDTH = 0x64, Register32 + PX_PLANE0_HEIGHT = 0x68, Register32 + PX_PLANE0_TILING_H = 0x6c, Register32 + PX_PLANE0_TILING_V = 0x70, Register32 + PX_PLANE0_STRIDE = 0x74, Register32 + PX_PLANE1_WIDTH = 0x78, Register32 + PX_PLANE1_HEIGHT = 0x7c, Register32 + PX_PLANE1_TILING_H = 0x80, Register32 + PX_PLANE1_TILING_V = 0x84, Register32 + PX_PLANE1_STRIDE = 0x88, Register32 + + INPUT_START1 = 0x8c, Register32 + INPUT_START2 = 0x90, Register32 + REG_0x94 = 0x94, Register32 + REG_0x98 = 0x98, Register32 + INPUT_END = 0x9c, Register32 + + OUTPUT_START1 = 0xa0, Register32 + OUTPUT_START2 = 0xa4, Register32 + OUTPUT_END = 0xa8, Register32 + + MATRIX_MULT = irange(0xAC, 11, 4), Register32 + DITHER = irange(0xD8, 10, 4), Register32 + + ENCODE_PIXEL_FORMAT = 0x100, R_ENCODE_PIXEL_FORMAT + # RGB888: R, G, B = byte pos + # RGB101010: R, G, B = 0/1/2 = low/mid/high bits + # RGB565: R, G, B = 0/1/2 = low/mid/high bits + # YUV10: Y, U, V = 0/1/2 = low/mid/high bits + # YUV linear: Y0 Cb Cr Y1 = byte pos + # YUV planar: Y U V = 0 for Y, 0/1 for U/V indicating position somehow + ENCODE_COMPONENT0_POS = 0x104, Register32 + ENCODE_COMPONENT1_POS = 0x108, Register32 + ENCODE_COMPONENT2_POS = 0x10c, Register32 + ENCODE_COMPONENT3_POS = 0x110, Register32 + + CONVERT_COLOR_SPACE = 0x114, Register32 + + REG_0x118 = 0x118, Register32 + REG_0x11c = 0x11c, Register32 + + REG_0x120 = 0x120, Register32 + + # details not understood yet + TILING_ENABLE = 0x124, Register32 + TILING_PLANE0 = 0x128, Register32 + TILING_PLANE1 = 0x12c, Register32 + + DECODE_MACROBLOCKS_W = 0x130, Register32 + DECODE_MACROBLOCKS_H = 0x134, Register32 + RIGHT_EDGE_PIXELS = 0x138, Register32 + BOTTOM_EDGE_PIXELS = 0x13c, Register32 + RIGHT_EDGE_SAMPLES = 0x140, Register32 + BOTTOM_EDGE_SAMPLES = 0x144, Register32 + + SCALE_FACTOR = 0x148, R_SCALE_FACTOR + + DECODE_PIXEL_FORMAT = 0x14c, R_DECODE_PIXEL_FORMAT + # 0 = Cb Y'0 Cr Y'1 1 = Y'0 Cb Y'1 Cr + YUV422_ORDER = 0x150, Register32 + # 0 = BGRA 1 = RGBA + RGBA_ORDER = 0x154, Register32 + RGBA_ALPHA = 0x158, Register32 + + PLANAR_CHROMA_HALVING = 0x15c, Register32 + + REG_0x160 = 0x160, Register32 + REG_0x164 = 0x164, Register32 + # REG_0x168 = 0x168, Register32 + REG_0x16c = 0x16c, Register32 + + REG_0x170 = 0x170, Register32 + # REG_0x174 = 0x174, Register32 + PERFCOUNTER = 0x178, Register32 + # REG_0x17c = 0x17c, Register32 + + # REG_0x180 = 0x180, Register32 + TIMEOUT = 0x184, Register32 + HWREV = 0x188, Register32 + + REG_0x18c = 0x18c, Register32 + REG_0x190 = 0x190, Register32 + REG_0x194 = 0x194, Register32 + REG_0x198 = 0x198, Register32 + REG_0x19c = 0x19c, Register32 + + ENABLE_RST_LOGGING = 0x1a0, Register32 + RST_LOG_ENTRIES = 0x1a4, Register32 + + REG_0x1a8 = 0x1a8, Register32 + REG_0x1ac = 0x1ac, Register32 + REG_0x1b0 = 0x1b0, Register32 + + REG_0x1b4 = 0x1b4, Register32 + REG_0x1b8 = 0x1b8, Register32 + REG_0x1bc = 0x1bc, Register32 + + REG_0x1c0 = 0x1c0, Register32 + REG_0x1c4 = 0x1c4, Register32 + + REG_0x1c8 = 0x1c8, Register32 + + REG_0x1cc = 0x1cc, Register32 + REG_0x1d0 = 0x1d0, Register32 + REG_0x1d4 = 0x1d4, Register32 + REG_0x1d8 = 0x1d8, Register32 + + REG_0x1dc = 0x1dc, Register32 + REG_0x1e0 = 0x1e0, Register32 + REG_0x1e4 = 0x1e4, Register32 + REG_0x1e8 = 0x1e8, Register32 + + REG_0x1ec = 0x1ec, Register32 + REG_0x1f0 = 0x1f0, Register32 + REG_0x1f4 = 0x1f4, Register32 + REG_0x1f8 = 0x1f8, Register32 + + REG_0x1fc = 0x1fc, Register32 + REG_0x200 = 0x200, Register32 + + REG_0x204 = 0x204, Register32 + REG_0x208 = 0x208, Register32 + + REG_0x20c = 0x20c, Register32 + REG_0x210 = 0x210, Register32 + REG_0x214 = 0x214, Register32 + REG_0x218 = 0x218, Register32 + + REG_0x21c = 0x21c, Register32 + REG_0x220 = 0x220, Register32 + + REG_0x224 = 0x224, Register32 + REG_0x228 = 0x228, Register32 + + REG_0x22c = 0x22c, Register32 + REG_0x230 = 0x230, Register32 + REG_0x234 = 0x234, Register32 + + REG_0x238 = 0x238, Register32 + REG_0x23c = 0x23c, Register32 + REG_0x240 = 0x240, Register32 + REG_0x244 = 0x244, Register32 + REG_0x248 = 0x248, Register32 + + REG_0x24c = 0x24c, Register32 + REG_0x250 = 0x250, Register32 + REG_0x254 = 0x254, Register32 + REG_0x258 = 0x258, Register32 + REG_0x25c = 0x25c, Register32 + + REG_0x260 = 0x260, Register32 + REG_0x264 = 0x264, Register32 + REG_0x268 = 0x268, Register32 + REG_0x26c = 0x26c, Register32 + + REG_0x280 = 0x280, Register32 + + JPEG_IO_FLAGS = 0x1000, R_JPEG_IO_FLAGS + REG_0x1004 = 0x1004, Register32 + REG_0x1008 = 0x1008, Register32 + QTBL_SEL = 0x100c, R_QTBL_SEL + + # fixme what _exactly_ does this control + HUFFMAN_TABLE = 0x1010, Register32 + RST_INTERVAL = 0x1014, Register32 # 16 bits effective + JPEG_HEIGHT = 0x1018, Register32 + JPEG_WIDTH = 0x101c, Register32 + + COMPRESSED_BYTES = 0x1020, Register32 + JPEG_OUTPUT_FLAGS = 0x1024, R_JPEG_OUTPUT_FLAGS + REG_0x1028 = 0x1028, Register32 + REG_0x102c = 0x102c, Register32 + + BITSTREAM_CORRUPTION = 0x1030, Register32 + # REG_0x1034 = 0x1034, Register32 + # REG_0x1038 = 0x1038, Register32 + # REG_0x103c = 0x103c, Register32 + + REG_0x1080 = 0x1080, Register32 + REG_0x1084 = 0x1084, Register32 + # REG_0x1088 = 0x1088, Register32 + REG_0x108c = 0x108c, Register32 + REG_0x1090 = 0x1090, Register32 + + SHIKINO_VERSION_MAGIC0 = 0x10e0, Register32 + SHIKINO_VERSION_MAGIC1 = 0x10e4, Register32 + SHIKINO_VERSION_MAGIC2 = 0x10e8, Register32 + SHIKINO_VERSION_MAGIC3 = 0x10ec, Register32 + SHIKINO_VERSION_MAGIC4 = 0x10f0, Register32 + # REG_0x10f4 = 0x10f4, Register32 + # REG_0x10f8 = 0x10f8, Register32 + # REG_0x10fc = 0x10fc, Register32 + + QTBL = irange(0x1100, 64, 4), Register32 + + # todo what's the format? + RSTLOG = irange(0x2000, 1024, 4), Register32 diff --git a/tools/proxyclient/m1n1/hw/mca.py b/tools/proxyclient/m1n1/hw/mca.py new file mode 100644 index 0000000..4c68a39 --- /dev/null +++ b/tools/proxyclient/m1n1/hw/mca.py @@ -0,0 +1,110 @@ +# SPDX-License-Identifier: MIT +from ..utils import * +from enum import IntEnum + +class R_STATUS(Register32): + EN = 0 + RST = 1 + +class R_MCLK_CONF(Register32): + SEL = 3, 0 + +class R_PORT_ENABLES(Register32): + CLOCK1 = 1 + CLOCK2 = 2 + DATA = 3 + +class R_PORT_CLKSEL(Register32): + SEL = 11, 8 + +class R_PORT_DATASEL(Register32): + TXA0 = 0 + TXA1 = 2 + TXA2 = 4 + TXA3 = 6 + TXA4 = 8 + TXA5 = 10 + + TXB0 = 1 + TXB1 = 3 + TXB2 = 5 + TXB3 = 7 + TXB4 = 9 + TXB5 = 11 + +class E_SLOT_WIDTH(IntEnum): + NONE = 0 + + W_16BIT = 0x4 + W_20BIT = 0x8 + W_24BIT = 0xc + W_32BIT = 0x10 + +class R_SERDES_CONF(Register32): + NSLOTS = 3, 0 + SLOT_WIDTH = 8, 4, E_SLOT_WIDTH + + BCLK_POL = 10 + LSB_FIRST = 11 + + UNK1 = 12 + UNK2 = 13 + IDLE_UNDRIVEN = 14 # TX only + NO_DATA_FEEDBACK = 15 # RX only + + SYNC_SEL = 18, 16 + +class R_INTMASK(Register32): + # macOS interested in 0x823c + UNK1 = 2 # m + UNK2 = 3 # m + UNK3 = 4 # m + TX_UNDERFLOW = 5 # m + + UNK4 = 9 # m + READ_SENSITIVE_UNK1 = 11 + READ_SENSITIVE_UNK2 = 15 # m + +class MCAClusterRegs(RegMap): + MCLK_STATUS = 0x0, R_STATUS + MCLK_CONF = 0x4, R_MCLK_CONF + + SYNCGEN_STATUS = 0x100, R_STATUS + SYNCGEN_MCLK_SEL = 0x104, Register32 + SYNCGEN_HI_PERIOD = 0x108, Register32 + SYNCGEN_LO_PERIOD = 0x10c, Register32 + + PORT_ENABLES = 0x600, R_PORT_ENABLES + PORT_CLK_SEL = 0x604, R_PORT_CLKSEL + PORT_DATA_SEL = 0x608, R_PORT_DATASEL + + INTSTATE = 0x700, R_INTMASK + INTMASK = 0x704, R_INTMASK + +class MCATXSerdesRegs(RegMap): + STATUS = 0x0, R_STATUS + CONF = 0x4, R_SERDES_CONF + BITDELAY = 0x8, Register32 + CHANMASK = irange(0xc, 4, 4), Register32 + +class MCARXSerdesRegs(RegMap): + STATUS = 0x0, R_STATUS + UNK1 = 0x4, Register32 + CONF = 0x8, R_SERDES_CONF + BITDELAY = 0xc, Register32 + CHANMASK = irange(0x10, 4, 4), Register32 + + +class MCACluster: + def __init__(self, u, base): + self.regs = MCAClusterRegs(u, base) + self.txa = MCATXSerdesRegs(u, base + 0x300) + self.txb = MCATXSerdesRegs(u, base + 0x500) + self.rxa = MCARXSerdesRegs(u, base + 0x200) + self.rxb = MCARXSerdesRegs(u, base + 0x400) + self.all_regs = [ + self.regs, + self.txa, self.txb, + self.rxa, self.rxb + ] + diff --git a/tools/proxyclient/m1n1/hw/nco.py b/tools/proxyclient/m1n1/hw/nco.py new file mode 100644 index 0000000..3d1840d --- /dev/null +++ b/tools/proxyclient/m1n1/hw/nco.py @@ -0,0 +1,89 @@ +# SPDX-License-Identifier: MIT + +__all__ = ["NCO"] + +def galois_lfsr(init, poly): + state = init + for i in range((1 << poly.bit_length() - 1) - 1): + if state & 1: + state = (state >> 1) ^ (poly >> 1) + else: + state = (state >> 1) + yield state + +def gen_lookup_tables(): + fwd, inv = dict(), dict() + lfsr_states = [0] + list(reversed(list(galois_lfsr(0x7ff, 0xa01)))) + for cycle, sr_state in enumerate(lfsr_states): + fwd[cycle + 2] = sr_state + inv[sr_state] = cycle + 2 + return fwd, inv + + +class NCOChannel: + def __init__(self, parent, base): + self.parent = parent + self.base = base + self.p = parent.u.proxy + + def enabled(self): + return bool(self.p.read32(self.base) & (1<<31)) + + def enable(self): + self.p.set32(self.base, 1<<31) + + def disable(self): + self.p.clear32(self.base, 1<<31) + + def set_rate(self, target): + was_enabled = self.enabled() + for off, val in enumerate(NCO.calc_regvals(self.parent.fin, target)): + self.p.write32(self.base + off*4, val) + if was_enabled: + self.enable() + + def get_rate(self): + return NCO.calc_rate(self.parent.fin, + [self.p.read32(self.base + off*4) for off in range(4)] + ) + + def __repr__(self): + return f"<NCO channel @ 0x{self.base:x}>" + + +class NCO: + TBL, TBL_INV = gen_lookup_tables() + + @classmethod + def calc_rate(self, fin, regvals): + try: + div = self.TBL_INV[regvals[1] >> 2] << 2 | regvals[1] & 3 + except KeyError: + raise ValueError("bad configuration") + inc1 = regvals[2] + inc2 = regvals[3] - 0x1_0000_0000 + return 2 * fin * (inc1 - inc2) // (div * (inc1 - inc2) + inc1) + + @classmethod + def calc_regvals(self, fin, fout): + div = 2 * fin // fout + inc1 = (2 * fin - div * fout) + inc2 = inc1 - fout + try: + return [0, self.TBL[div >> 2] << 2 | div & 3, inc1, inc2 + 0x1_0000_0000] + except KeyError: + raise ValueError("target rate out of range") + + def __init__(self, u, devpath, stride=0x4000): + self.u = u + node = u.adt[devpath] + self.fin = u.adt["/arm-io"].clock_frequencies[node.clock_ids[0] - 256] + + reg = node.get_reg(0) + self.chans = [ + NCOChannel(self, base) + for base in range(reg[0], reg[0] + reg[1], stride) + ] + + def __getitem__(self, idx): + return self.chans[idx] diff --git a/tools/proxyclient/m1n1/hw/pmgr.py b/tools/proxyclient/m1n1/hw/pmgr.py new file mode 100644 index 0000000..85ba247 --- /dev/null +++ b/tools/proxyclient/m1n1/hw/pmgr.py @@ -0,0 +1,71 @@ +# SPDX-License-Identifier: MIT +from ..utils import * + +class R_PSTATE(Register32): + RESET = 31 + AUTO_ENABLE = 28 + AUTO_STATE = 27, 24 + PARENT_MISSING = 11 + DEV_DISABLE = 10 + WAS_CLKGATED = 9 + WAS_PWRGATED = 8 + ACTUAL = 7, 4 + DESIRED = 3, 0 + +class R_PWRGATE(Register32): + GATE = 31 + +class R_CLK_CFG(Register32): + UNK31 = 31 + SRC = 30, 24 + UNK20 = 20 + UNK8 = 8 + UNK0 = 7, 0 + +class PMGRRegs0(RegMap): + PS3 = irange(0x0000, 10, 8), R_PSTATE + PS4 = irange(0x0200, 32, 8), R_PSTATE + PS5 = irange(0x0300, 32, 8), R_PSTATE + PS6 = irange(0x0c00, 2, 8), R_PSTATE + PS7 = irange(0x4000, 13, 8), R_PSTATE + PS8 = irange(0x8000, 5, 8), R_PSTATE + PS9 = irange(0xc000, 7, 8), R_PSTATE + PS10 = irange(0x10000, 10, 8), R_PSTATE + PS11 = irange(0x100, 32, 8), R_PSTATE + PS12 = irange(0x400, 15, 8), R_PSTATE + + PG1 = irange(0x1c010, 16, 8), R_PWRGATE + PG1CFG = (irange(0x1c090, 69, 24), irange(0, 6, 4)), Register32 + + CPUTVM0 = 0x48000, Register32 + CPUTVM1 = 0x48c00, Register32 + CPUTVM2 = 0x48800, Register32 + CPUTVM3 = 0x48400, Register32 + +class PMGRRegs1(RegMap): + PS0 = irange(0x58, 32, 8), R_PSTATE + PS1 = irange(0x4000, 32, 8), R_PSTATE + PS2 = irange(0x8000, 32, 8), R_PSTATE + + PG0 = irange(0x1c010, 32, 8), R_PWRGATE + +class PMGRRegs2(RegMap): + CLK_CFG0 = irange(0x40000, 86, 4), R_CLK_CFG + CLK_CFG1 = irange(0x40200, 8, 4), R_CLK_CFG + CLK_CFG2 = irange(0x40280, 2, 4), R_CLK_CFG + +class PMGR: + def __init__(self, u): + self.u = u + self.p = u.proxy + self.iface = u.iface + self.node = u.adt["/arm-io/pmgr"] + self.regs = [ + PMGRRegs0(u, self.node.get_reg(0)[0]), + PMGRRegs1(u, self.node.get_reg(1)[0]), + PMGRRegs2(u, self.node.get_reg(2)[0]), + ] + + def dump_all(self): + for i in self.regs: + i.dump_regs() diff --git a/tools/proxyclient/m1n1/hw/pmu.py b/tools/proxyclient/m1n1/hw/pmu.py new file mode 100644 index 0000000..b545df4 --- /dev/null +++ b/tools/proxyclient/m1n1/hw/pmu.py @@ -0,0 +1,36 @@ +# SPDX-License-Identifier: MIT +import struct + +from ..utils import * +from .spmi import SPMI + +__all__ = ["PMU"] + +class PMU: + + def __init__(self, u, adt_path=None): + self.u = u + if adt_path is None: + adt_path = PMU.find_primary_pmu(u.adt) + + self.node = u.adt[adt_path] + self.spmi = SPMI(u, adt_path.rpartition('/')[0]) + self.adt_path = adt_path + self.primary = u.adt[adt_path].is_primary == 1 + self.reg = u.adt[adt_path].reg[0] + + def reset_panic_counter(self): + if self.primary: + leg_scrpad = self.node.info_leg__scrpad[0] + self.spmi.write8(self.reg, leg_scrpad + 2, 0) # error counts + + @staticmethod + def find_primary_pmu(adt): + for child in adt["/arm-io"]: + if child.name.startswith("nub-spmi"): + for pmu in child: + compat = getattr(pmu, "compatible")[0] if hasattr(pmu, "compatible") else "unset" + primary = (getattr(pmu, "is-primary") == 1) if hasattr(pmu, "is-primary") else False + if compat == "pmu,spmi" and primary: + return pmu._path.removeprefix('/device-tree') + raise KeyError(f"primary 'pmu,spmi' node not found") diff --git a/tools/proxyclient/m1n1/hw/prores.py b/tools/proxyclient/m1n1/hw/prores.py new file mode 100644 index 0000000..5985e72 --- /dev/null +++ b/tools/proxyclient/m1n1/hw/prores.py @@ -0,0 +1,250 @@ +# SPDX-License-Identifier: MIT +from ..utils import * +from collections import namedtuple +from enum import IntEnum + + +EncodeNotRawDescriptor = namedtuple('EncodeNotRawDescriptor', [ + 'flags', # +0x000 + # [31:16] ????? + # 13 ????? + # 12 ????? + # [11:9] ????? + # 8 - enable alpha + # 7 - alpha channel bpp + # 0 -> 8bpp + # 1 -> 16bpp + # 6 - something unknown about tiling + # [5:4] ????? + # [3:2] - chroma subsampling + # 00 -> broken? + # 01 -> broken? + # 10 -> 4:2:2 + # 11 -> 4:4:4 + # [1:0] - input bpp + # 00 -> 8bpp + # 01 -> 16bpp? + # 10 -> 16bpp? + # 11 -> 16bpp? + # the last three all produce slightly differnet outputs + # so might be 10/12/14/16????? + 'flags2', # +0x004 + 'output_iova', # +0x008 + 'max_out_sz', # +0x010 + 'offset_x', # +0x014 + 'offset_y', # +0x016 + 'pix_surface_w_2_', # +0x018 + 'pix_surface_h_2_', # +0x01a + 'pix_surface_w', # +0x01c + 'pix_surface_h', # +0x01e + 'luma_stride', # +0x020 + 'chroma_stride', # +0x022 + 'alpha_stride', # +0x024 + 'unk_pad_0x26_', # +0x026 + 'luma_iova', # +0x028 + 'pix_plane0_tileheader_thing_', # +0x030 + 'chroma_iova', # +0x038 + 'pix_plane1_tileheader_thing_', # +0x040 + 'alpha_iova', # +0x048 + 'pix_plane2_tileheader_thing_', # +0x050 + 'frame_header_sz', # +0x058 + 'unk_pad_0x5a_', # +0x05a + 'bitstream_version', # +0x05b + 'encoder_identifier', # +0x05c + 'pix_surface_w_byteswap_', # +0x060 + 'pix_surface_h_byteswap_', # +0x062 + 'chroma_format_interlace_mode', # +0x064 + 'aspect_ratio_frame_rate', # +0x065 + 'color_primaries', # +0x066 + 'transfer_characteristic', # +0x067 + 'matrix_coefficients', # +0x068 + 'alpha_channel_type', # +0x069 + 'frame_hdr_reserved14', # +0x06a + 'unk_pad_0x6c_', # +0x06c + 'deprecated_number_of_slices', # +0x0ec + 'log2_desired_slice_size_in_mb', # +0x0ee + 'quantization_index', # +0x0ef + 'unk_0xf0_', # +0x0f0 + 'unk_0xf2_', # +0x0f2 + 'unk_0xf4_', # +0x0f4 + 'unk_0xfc_', # +0x0fc + 'unk_0x100_0_', # +0x100 + 'unk_0x100_1_', # +0x104 + 'unk_0x100_2_', # +0x108 + 'unk_0x100_3_', # +0x10c + 'unk_0x110_0_', # +0x110 + 'unk_0x110_1_', # +0x114 + 'unk_0x110_2_', # +0x118 + 'unk_0x110_3_', # +0x11c + 'unk_0x110_4_', # +0x120 + 'unk_0x110_5_', # +0x124 + 'unk_0x110_6_', # +0x128 + 'unk_0x110_7_', # +0x12c + 'unk_0x110_8_', # +0x130 + 'unk_0x110_9_', # +0x134 + 'unk_0x110_10_', # +0x138 + 'unk_0x110_11_', # +0x13c + 'unk_0x110_12_', # +0x140 + 'unk_0x110_13_', # +0x144 + 'unk_0x110_14_', # +0x148 + 'unk_0x110_15_', # +0x14c + 'quant_table_sel', # +0x150 + # upper nibble: quality / table index + # lower nibble UNKNOWN! + 'unk_pad_0x154_', # +0x154 +]) +ENCODE_NOT_RAW_STRUCT = "<IIQIHHHHHHHHH2sQQQQQQH1sBIHHBBBBBB2s128sHBBHHQIIIIIIIIIIIIIIIIIIIIII44s" + + +class ProResRegs(RegMap): + # something reads + REG_0x0 = 0x000, Register32 + MODE = 0x008, Register32 # 4 bits + IRQ_ENABLE = 0x00c, Register32 # 2 bits + IRQ_STATUS = 0x010, Register32 + + ST0 = 0x014, Register32 # interrupt handler reads + ST1 = 0x018, Register32 # interrupt handler reads + REG_0x1c = 0x01c, Register32 # interrupt handler reads + REG_0x38 = 0x038, Register32 # exists, maybe RO + REG_0x3c = 0x03c, Register32 # interrupt handler reads + REG_0x40 = 0x040, Register32 # exists, maybe RO, looks like 0x44 + REG_0x44 = 0x044, Register32 # interrupt handler reads + REG_0x48 = 0x048, Register32 # exists, maybe RO, looks like 0x44 + REG_0x4c = 0x04c, Register32 # exists, maybe RO, looks like 0x44 + REG_0x50 = 0x050, Register32 # exists, maybe RO, looks like 0x44 + REG_0x54 = 0x054, Register32 # exists, maybe RO, looks like 0x44 + + DR_SIZE = 0x100, Register32 + DR_ADDR_LO = 0x104, Register32 + DR_ADDR_HI = 0x108, Register32 + DR_HEAD = 0x10c, Register32 # bit24 is special, something about wrapping around? + DR_TAIL = 0x110, Register32 + + # This giant block may or may not be touched by tunables + # Function is all unknown + REG_0x114 = 0x114, Register32 # can set bits 0000FFFF + REG_0x118 = 0x118, Register32 # can set bits 07FF07FF + + REG_0x134 = 0x134, Register32 # can set bits 00000003 + + REG_0x144 = 0x144, Register32 # can set bits 00000001 + REG_0x148 = 0x148, Register32 # can set bits 00000001 + + REG_0x160 = 0x160, Register32 # can set bits BFFF3FFF + REG_0x164 = 0x164, Register32 # can set bits 07FF07FF + + REG_0x170 = 0x170, Register32 # can set bits BFFF3FFF + REG_0x174 = 0x174, Register32 # can set bits 07FF07FF + + REG_0x180 = 0x180, Register32 # can set bits BFFF3FFF + REG_0x184 = 0x184, Register32 # can set bits 07FF07FF + + REG_0x190 = 0x190, Register32 # can set bits BFFF3FFF + REG_0x194 = 0x194, Register32 # can set bits 000000FF + REG_0x198 = 0x198, Register32 # RO? init value 07FB066F + + REG_0x1a0 = 0x1a0, Register32 # can set bits BFFF3FFF + REG_0x1a4 = 0x1a4, Register32 # can set bits 000000FF + REG_0x1a8 = 0x1a8, Register32 # RO? init value 037C03EE + + REG_0x1b0 = 0x1b0, Register32 # can set bits BFFF3FFF + REG_0x1b4 = 0x1b4, Register32 # can set bits 000000FF + REG_0x1b8 = 0x1b8, Register32 # RO? init value 04E00377 + + REG_0x1c0 = 0x1c0, Register32 # can set bits BFFF3FFF + REG_0x1c4 = 0x1c4, Register32 # can set bits 000000FF + REG_0x1c8 = 0x1c8, Register32 # RO? init value 051C00DA + + REG_0x1d0 = 0x1d0, Register32 # can set bits BFFF3FFF + REG_0x1d4 = 0x1d4, Register32 # can set bits 000000FF + REG_0x1d8 = 0x1d8, Register32 # can set bits 000000FF + REG_0x1dc = 0x1dc, Register32 # can set bits 00FFFFFF + + REG_0x1ec = 0x1ec, Register32 # can set bits FFFFFFFF + + REG_0x270 = 0x270, Register32 # can set bits BFFF3FFF + REG_0x274 = 0x274, Register32 # can set bits 07FF07FF + REG_0x278 = 0x278, Register32 # can set bits FFFFFFC0 + REG_0x27c = 0x27c, Register32 # can set bits 000003FF + REG_0x280 = 0x280, Register32 # can set bits FFFFFFC0 + REG_0x284 = 0x284, Register32 # can set bits FFFFFFC0 + REG_0x28c = 0x28c, Register32 # can set bits FFFFFFC0 + + REG_0x290 = 0x290, Register32 # can set bits BFFF3FFF + REG_0x294 = 0x294, Register32 # can set bits 000000FF + REG_0x298 = 0x298, Register32 # RO? init value 07FB066F + + REG_0x2a0 = 0x2a0, Register32 # can set bits BFFF3FFF + REG_0x2a4 = 0x2a4, Register32 # can set bits 000000FF + REG_0x2a8 = 0x2a8, Register32 # RO? init value 037C03EE + + REG_0x2b0 = 0x2b0, Register32 # can set bits BFFF3FFF + REG_0x2b4 = 0x2b4, Register32 # can set bits 000000FF + REG_0x2b8 = 0x2b8, Register32 # RO? init value 04E00377 + + REG_0x2c0 = 0x2c0, Register32 # can set bits BFFF3FFF + REG_0x2c4 = 0x2c4, Register32 # can set bits 000000FF + REG_0x2c8 = 0x2c8, Register32 # RO? init value 051C00DA + + REG_0x2d0 = 0x2d0, Register32 # can set bits FFFFFFFD, CANNOT clear 00000011 + REG_0x2d4 = 0x2d4, Register32 # can set bits 00000001 + REG_0x2d8 = 0x2d8, Register32 # can set bits FFFF0007 + REG_0x2dc = 0x2dc, Register32 # RO? init value 07FB066F + REG_0x2e0 = 0x2e0, Register32 # can set bits 07FF07FF + + REG_0x2f0 = 0x2f0, Register32 # can set bits FFFFFFFF + + REG_0x2f8 = 0x2f8, Register32 # can set bits FFFFFFFD, CANNOT clear 00000011 + REG_0x2fc = 0x2fc, Register32 # can set bits 00000001 + REG_0x300 = 0x300, Register32 # can set bits FFFF0007 + REG_0x304 = 0x304, Register32 # RO? init value 037C03EE + REG_0x308 = 0x308, Register32 # can set bits 07FF07FF + + REG_0x318 = 0x318, Register32 # can set bits FFFFFFFF + + REG_0x320 = 0x320, Register32 # can set bits FFFFFFFD, CANNOT clear 00000011 + REG_0x324 = 0x324, Register32 # can set bits 00000001 + REG_0x328 = 0x328, Register32 # can set bits FFFF0007 + REG_0x32c = 0x32c, Register32 # RO? init value 04E00377 + REG_0x330 = 0x330, Register32 # can set bits 07FF07FF + + REG_0x340 = 0x340, Register32 # can set bits FFFFFFFF + + REG_0x350 = 0x350, Register32 # can set bits BFFF3FFF + REG_0x354 = 0x354, Register32 # can set bits 07FF07FF + REG_0x358 = 0x358, Register32 # can set bits FFFFFFC0 + REG_0x35c = 0x35c, Register32 # can set bits 000003FF + REG_0x360 = 0x360, Register32 # can set bits FFFFFFC0 + REG_0x364 = 0x364, Register32 # can set bits FFFFFFC0 + REG_0x368 = 0x368, Register32 # can set bits FFFFFFC0 + + REG_0x370 = 0x370, Register32 # can set bits BFFF3FFF + REG_0x374 = 0x374, Register32 # can set bits 07FF07FF + + + QUANT_LUMA_EHQ = irange(0x0800, 32, 4), Register32 + QUANT_LUMA_HQ = irange(0x0880, 32, 4), Register32 + QUANT_LUMA_NQ = irange(0x0900, 32, 4), Register32 + QUANT_LUMA_LT = irange(0x0980, 32, 4), Register32 + QUANT_LUMA_PROXY = irange(0x0A00, 32, 4), Register32 + QUANT_CHROMA_EHQ = irange(0x1000, 32, 4), Register32 + QUANT_CHROMA_HQ = irange(0x1080, 32, 4), Register32 + QUANT_CHROMA_NQ = irange(0x1100, 32, 4), Register32 + QUANT_CHROMA_LT = irange(0x1180, 32, 4), Register32 + QUANT_CHROMA_PROXY = irange(0x1200, 32, 4), Register32 + + # wtf, writing to this doesn't actually work? do we have to enable it? + DC_QUANT_SCALE = irange(0x1800, 112, 4), Register32 + + REG_0x19c0 = 0x19c0, Register32 # unknown, all 1s, RO? + REG_0x19c4 = 0x19c4, Register32 # unknown, all 1s, RO? + REG_0x19c8 = 0x19c8, Register32 # unknown, all 1s, RO? + REG_0x19cc = 0x19cc, Register32 # unknown, all 1s, RO? + REG_0x19d0 = 0x19d0, Register32 # unknown, all 1s, RO? + REG_0x19d4 = 0x19d4, Register32 # unknown, all 1s, RO? + REG_0x19d8 = 0x19d8, Register32 # unknown, all 1s, RO? + REG_0x19dc = 0x19dc, Register32 # unknown, can set bits 00000001 + + # Unknown, inits to 0x12345678, can R/W + REG_0x1A00 = 0x1a00, Register32 diff --git a/tools/proxyclient/m1n1/hw/scaler.py b/tools/proxyclient/m1n1/hw/scaler.py new file mode 100644 index 0000000..714f366 --- /dev/null +++ b/tools/proxyclient/m1n1/hw/scaler.py @@ -0,0 +1,212 @@ +# SPDX-License-Identifier: MIT +from ..utils import * +from enum import IntEnum + + +### NOTE: This is "MSR10j" (M1 Max), and there definitely *ARE* differences from M1 + +class R_IRQS(Register32): + DONE = 0 + DBGSTS_ERROR = 1 + READ_ERROR = 3 + # This doesn't actually trigger on bad IOVAs? + WRITE_ERROR_MAYBE = 4 + DECOMPRESSION_ERROR = 9 + CONTEXT_SWITCH = 10 + _BIT11 = 11 + AXI_ERROR = 12 + _BIT13 = 13 + + +class E_ROTATE(IntEnum): + # clockwise rotations + ROT_0 = 0 + ROT_90 = 1 + ROT_180 = 2 + ROT_270 = 3 + + +class R_FLIP_ROTATE(Register32): + ROTATE = 1, 0, E_ROTATE + FLIP_UPDOWN = 2 + FLIP_LEFTRIGHT = 3 + + +class R_SCALE_FLAGS(Register32): + EN = 0 + MAKE_THE_OUTPUT_YELLOW = 1 + # only when bit1 is set, only on H scaling + MAKE_A_BLUE_LINE_APPEAR = 4 + + +class ScalerMainRegs(RegMap): + # on startup 1 will be written followed by 0 + # but it's not clear if that actually does anything + HW_VERSION = 0x00000, Register32 + # bit0 = normal, bit1 = apiodma related + # if things are reset here, reading all other regs will *HANG* + RESET = 0x00004, Register32 + # can set 0x1f00 + RESET_APIODMA_RELATED = 0x00008, Register32 + IS_RUNNING = 0x0000c, Register32 + # writable, can set to 0xfff + REG_0x10 = 0x00010, Register32 + REGISTER_FIFO_AVAILABILITY = 0x00014, Register32 + # XNU sets 0x121b, we can at most set 0x3e1b + IRQ_ENABLE = 0x00018, R_IRQS + MSR_GLBL_IRQSTS = 0x0001c, R_IRQS + FRAME_COUNT = 0x00020, Register32 + + # can set 7 + REG_0x58 = 0x00058, Register32 + + # can set 0xffff + REG_0x74 = 0x00074, Register32 + + # 1, or 3 if readonly?? + START = 0x00080, Register32 + # can set all bits + REG_0x84 = 0x00084, Register32 + # can set all bits + REG_0x88 = 0x00088, Register32 + # can set 0x8000ffff + REG_0x8c = 0x0008c, Register32 + + # can set all bits + REG_0x98 = 0x00098, Register32 + # 0x3f3d? + MSR_CTRL_DBGSTS = 0x0009c, Register32 + # can set 3 + REG_0xa0 = 0x000a0, Register32 + PROFILING_RELATED = 0x000a4, Register32 + + # Can set bits 0/1/2 + # Does something breaking horizontal scaling + # bit2 seems to affect alpha output + PIXEL_AVERAGING = 0x000e4, Register32 + + TRANSFORM_ID = 0x00110, Register32 + + RDMA_THING0 = 0x00180, Register32 + RDMA_THING1 = 0x00184, Register32 + RDMA_THING2 = 0x00188, Register32 + RDMA_THING3 = 0x0018c, Register32 + RDMA_THING4 = 0x00190, Register32 + + # there's probably another source plane existing? + SRC_PLANE0_LO = 0x00198, Register32 + SRC_PLANE0_HI = 0x0019c, Register32 + SRC_PLANE1_LO = 0x001a0, Register32 + SRC_PLANE1_HI = 0x001a4, Register32 + SRC_PLANE2_LO = 0x001a8, Register32 + SRC_PLANE2_HI = 0x001ac, Register32 + + SRC_PLANE0_COMPRESSEDTHING_LO = 0x001b8, Register32 + SRC_PLANE0_COMPRESSEDTHING_HI = 0x001bc, Register32 + SRC_PLANE1_COMPRESSEDTHING_LO = 0x001c0, Register32 + SRC_PLANE1_COMPRESSEDTHING_HI = 0x001c4, Register32 + SRC_PLANE2_COMPRESSEDTHING_LO = 0x001c8, Register32 + SRC_PLANE2_COMPRESSEDTHING_HI = 0x001cc, Register32 + + SRC_PLANE0_STRIDE = 0x001d8, Register32 + SRC_PLANE1_STRIDE = 0x001dc, Register32 + SRC_PLANE2_STRIDE = 0x001e0, Register32 + + # seems to be in "pixels" + SRC_PLANE0_OFFSET = 0x001e8, Register32 + SRC_PLANE1_OFFSET = 0x001ec, Register32 + SRC_PLANE2_OFFSET = 0x001f0, Register32 + + SRC_SWIZZLE = 0x001f8, Register32 + SRC_W = 0x001fc, Register32 + SRC_H = 0x00200, Register32 + CACHE_HINTS_THING0 = irange(0x00204, 4, 4), Register32 + CACHE_HINTS_THING1 = irange(0x00214, 4, 4), Register32 + TUNABLES_THING0 = irange(0x00224, 4, 4), Register32 + SRC_SIZE_THING2 = 0x00234, Register32 + SRC_SIZE_THING3 = 0x00238, Register32 + SRC_SIZE_THING4 = 0x0023c, Register32 + + SRC_SIZE_THING5 = 0x00244, Register32 + SRC_SIZE_THING6 = 0x00248, Register32 + SRC_SIZE_THING7 = 0x0024c, Register32 + + WDMA_THING0 = 0x00280, Register32 + WDMA_THING1 = 0x00284, Register32 + WDMA_THING2 = 0x00288, Register32 + WDMA_THING3 = 0x0028c, Register32 + DST_PLANE0_LO = 0x00290, Register32 + DST_PLANE0_HI = 0x00294, Register32 + DST_PLANE1_LO = 0x00298, Register32 + DST_PLANE1_HI = 0x0029c, Register32 + DST_PLANE2_LO = 0x002a0, Register32 + DST_PLANE2_HI = 0x002a4, Register32 + DST_PLANE0_COMPRESSEDTHING_LO = 0x002a8, Register32 + DST_PLANE0_COMPRESSEDTHING_HI = 0x002ac, Register32 + DST_PLANE1_COMPRESSEDTHING_LO = 0x002b0, Register32 + DST_PLANE1_COMPRESSEDTHING_HI = 0x002b4, Register32 + DST_PLANE2_COMPRESSEDTHING_LO = 0x002b8, Register32 + DST_PLANE2_COMPRESSEDTHING_HI = 0x002bc, Register32 + DST_PLANE0_STRIDE = 0x002c0, Register32 + DST_PLANE1_STRIDE = 0x002c4, Register32 + DST_PLANE2_STRIDE = 0x002c8, Register32 + DST_PLANE0_OFFSET = 0x002cc, Register32 + DST_PLANE1_OFFSET = 0x002d0, Register32 + DST_PLANE2_OFFSET = 0x002d4, Register32 + DST_SWIZZLE = 0x002d8, Register32 + DST_W = 0x002dc, Register32 + DST_H = 0x002e0, Register32 + # uhh is there a macos bug with these? last val always overwritten + CACHE_HINTS_THING2 = irange(0x002e4, 3, 4), Register32 + CACHE_HINTS_THING3 = irange(0x002f0, 3, 4), Register32 + TUNABLES_THING1 = irange(0x002fc, 3, 4), Register32 + DST_SIZE_THING2 = 0x00308, Register32 + DST_SIZE_THING3 = 0x0030c, Register32 + DST_SIZE_THING4 = 0x00310, Register32 + DST_SIZE_THING5 = 0x00314, Register32 + DST_SIZE_THING6 = 0x00318, Register32 + DST_SIZE_THING7 = 0x0031c, Register32 + + FLIP_ROTATE = 0x00380, R_FLIP_ROTATE + + # can set bit 0/1 + # the output obviously changes when this is set + PSEUDO_LINEAR_SCALING = 0x00480, Register32 + + SCALE_V_FLAGS = 0x01000, R_SCALE_FLAGS + # No idea what a DDA is? Q1.22 or U1.22 (23 bits total) + # Also macOS doesn't touch a bunch of the V ones (uses H instead???) + SCALE_V_DDA_THING0 = 0x01004, Register32 + SCALE_V_DDA_THING1 = 0x01008, Register32 + # Q4.22 or U4.22 (26 bits total) + SCALE_V_RATIO_0 = 0x0100c, Register32 + SCALE_V_RATIO_1 = 0x01010, Register32 + SCALE_V_RATIO_2 = 0x01014, Register32 + SCALE_V_RATIO_3 = 0x01018, Register32 + SCALE_V_DDA_THING2 = 0x0101c, Register32 + SCALE_V_RATIO_4 = 0x01020, Register32 + SCALE_V_RATIO_5 = 0x01024, Register32 + + # 9 taps, 32 phases polyphase resampling filter + # Q4.12 (16-bit total) fixed point filter coeffs + # packed into 32-bit registers, 3 sets of filters total (chroma/luma/alpha??) + # exact ordering and arithmetic performed not yet clear + SCALE_FILTER_V_BLOCK0 = irange(0x01400, 9 * 32, 4), Register32 + SCALE_FILTER_V_BLOCK1 = irange(0x01c00, 9 * 32 // 2, 4), Register32 + + SCALE_H_FLAGS = 0x02000, R_SCALE_FLAGS + # No idea what a DDA is? Q1.22 or U1.22 (23 bits total) + SCALE_H_DDA_THING0 = 0x02004, Register32 + SCALE_H_DDA_THING1 = 0x02008, Register32 + # Q4.22 or U4.22 (26 bits total) + SCALE_H_RATIO_0 = 0x0200c, Register32 + SCALE_H_RATIO_1 = 0x02010, Register32 + SCALE_H_RATIO_2 = 0x02014, Register32 + SCALE_H_RATIO_3 = 0x02018, Register32 + SCALE_H_DDA_THING2 = 0x0201c, Register32 + SCALE_H_RATIO_4 = 0x02020, Register32 + SCALE_H_RATIO_5 = 0x02024, Register32 + + # 15 taps, 32 phases polyphase resampling filter + SCALE_FILTER_H_BLOCK0 = irange(0x02400, 15 * 32, 4), Register32 + SCALE_FILTER_H_BLOCK1 = irange(0x02c00, 15 * 32 // 2, 4), Register32 diff --git a/tools/proxyclient/m1n1/hw/sep.py b/tools/proxyclient/m1n1/hw/sep.py new file mode 100644 index 0000000..f206a71 --- /dev/null +++ b/tools/proxyclient/m1n1/hw/sep.py @@ -0,0 +1,169 @@ +# SPDX-License-Identifier: MIT +import struct +from collections import defaultdict, deque +from enum import IntEnum + +from ..trace.asc import ASCRegs +from ..utils import * + + +class BootRomMsg(IntEnum): + GET_STATUS = 2 + BOOT_TZ0 = 5 + BOOT_IMG4 = 6 + SET_SHMEM = 0x18 + + +class BootRomStatus(IntEnum): + STATUS_OK = 0x66 + STATUS_BOOT_TZ0_DONE = 0x69 + STATUS_BOOT_IMG4_DONE = 0x6A + STATUS_BOOT_UNK_DONE = 0xD2 + + +class SEPMessage(Register64): + EP = 7, 0 + TAG = 15, 8 + TYPE = 23, 16 + PARAM = 31, 24 + DATA = 63, 32 + + +# TODO: make this class actually own the shared memory instead of just +# generating a static buffer if we actually need to read/write to +# individual items inside the shmem buffer +class SEPShMem: + def __init__(self): + self.items = [] + self.offset = 0x4000 + + def add_item(self, name, data, min_size=0): + sz = align_up(len(data) + 4, 0x4000) + sz = max(sz, min_size) + self.items.append((name, self.offset, sz, struct.pack("<I", len(data)) + data)) + self.offset += sz + + def finalize(self): + bfr = bytearray(b"\x00" * self.offset) + for i, (name, offset, sz, data) in enumerate(self.items): + bfr[i * 16 : i * 16 + 12] = struct.pack("<4sII", name, sz, offset) + bfr[offset : offset + len(data)] = data + + cnt = len(self.items) + bfr[cnt * 16 : cnt * 16 + 4] = b"llun" # null + + return bfr + + +class SEP: + SHMEM_IOVA = 0xBEEF0000 + FW_IOVA = 0xDEAD0000 + + def __init__(self, proxy, iface, utils): + self.i = iface + self.p = proxy + self.u = utils + + self.sep_base = self.u.adt["/arm-io/sep"].get_reg(0)[0] + self.dart_base = self.u.adt["/arm-io/dart-sep"].get_reg(0)[0] + + self.asc = ASCRegs(self.u, self.sep_base) + + self.dart_handle = self.p.dart_init(self.dart_base, 0) + + self.epnum2name = {} + self.epname2num = {} + self.msgs = defaultdict(deque) + + def map_sepfw(self): + sepfw_addr, sepfw_size = self.u.adt["/chosen/memory-map"].SEPFW + self.p.dart_map(self.dart_handle, self.FW_IOVA, sepfw_addr, sepfw_size) + + def unmap_sepfw(self): + _, sepfw_size = self.u.adt["/chosen/memory-map"].SEPFW + self.p.dart_unmap(self.dart_handle, self.FW_IOVA, sepfw_size) + + def create_shmem(self): + shmem = SEPShMem() + + # PNIC - panic buffer + shmem.add_item(b"CINP", b"\x00", 0x8000) + + # ALPO / SIPS - unknown img4-like blobs from the ADT + addr, sz = self.u.adt["/chosen/boot-object-manifests"].lpol + shmem.add_item(b"OPLA", self.i.readmem(addr, sz)) + addr, sz = self.u.adt["/chosen/boot-object-manifests"].ibot + shmem.add_item(b"IPIS", self.i.readmem(addr, sz)) + + bfr = shmem.finalize() + sz = align_up(len(bfr), 0x4000) + self.shmem = self.u.heap.memalign(0x4000, 0x30000) + self.i.writemem(self.shmem, bfr) + self.p.dart_map(self.dart_handle, self.SHMEM_IOVA, self.shmem, 0x30000) + + def boot(self): + self.create_shmem() + self.map_sepfw() + + self.send_msg(SEPMessage(EP=0xFF, TYPE=BootRomMsg.GET_STATUS)) + self.expect_msg(0xFF, BootRomStatus.STATUS_OK) + + self.send_msg(SEPMessage(EP=0xFF, TYPE=BootRomMsg.BOOT_TZ0)) + self.expect_msg(0xFF, BootRomStatus.STATUS_BOOT_TZ0_DONE) + self.expect_msg(0xFF, BootRomStatus.STATUS_BOOT_UNK_DONE) + + self.send_msg(SEPMessage(EP=0xFF, TYPE=BootRomMsg.GET_STATUS)) + self.expect_msg(0xFF, BootRomStatus.STATUS_OK) + + self.send_msg( + SEPMessage(EP=0xFF, TYPE=BootRomMsg.BOOT_IMG4, DATA=self.FW_IOVA >> 0xC) + ) + self.send_msg( + SEPMessage(EP=0xFE, TYPE=BootRomMsg.SET_SHMEM, DATA=self.SHMEM_IOVA >> 0xC) + ) + + self.expect_msg(0xFF, BootRomStatus.STATUS_BOOT_IMG4_DONE) + + self.unmap_sepfw() + + def expect_msg(self, ep, type): + msg = self.recv_msg(ep, block=True) + if msg.TYPE != type: + raise ValueError( + f"Expected type 0x{type:x} but got message with type 0x{msg.TYPE:x}" + ) + + def send_msg(self, msg): + self.asc.INBOX0 = msg.value + self.asc.INBOX1 = 0 + + def _recv_single_msg(self): + msg = SEPMessage(self.asc.OUTBOX0.val) + _ = self.asc.OUTBOX1.val + return msg + + def _try_recv_msgs(self): + while not self.asc.OUTBOX_CTRL.reg.EMPTY: + msg = self._recv_single_msg() + self.msgs[msg.EP].append(msg) + self._handle_ep_discovery() + + def _handle_ep_discovery(self): + while len(self.msgs[0xFD]): + msg = self.msgs[0xFD].popleft() + if msg.TYPE == 0: + cs = "".join( + [chr((msg.DATA >> (i * 8)) & 0xFF) for i in range(3, -1, -1)] + ) + self.epnum2name[msg.PARAM] = cs + self.epname2num[cs] = msg.PARAM + + def recv_msg(self, ep, block=False): + self._try_recv_msgs() + while block and len(self.msgs[ep]) < 1: + self._try_recv_msgs() + + if len(self.msgs[ep]): + return self.msgs[ep].popleft() + else: + return None diff --git a/tools/proxyclient/m1n1/hw/spi.py b/tools/proxyclient/m1n1/hw/spi.py new file mode 100644 index 0000000..1fa9be5 --- /dev/null +++ b/tools/proxyclient/m1n1/hw/spi.py @@ -0,0 +1,147 @@ +# SPDX-License-Identifier: MIT +from ..utils import * + +__all__ = ["SPIRegs"] + +class R_CTRL(Register32): + RX_FIFO_RESET = 3 + TX_FIFO_RESET = 2 + RUN = 0 + +class R_CFG(Register32): + # impl: 002fb1e6 + IE_TX_COMPLETE = 21 + b19 = 19 + FIFO_THRESH = 18, 17 + # 0 = 8 bytes + # 1 = 4 bytes + # 2 = 1 byte + # 3 = disabled + WORD_SIZE = 16, 15 + # 0 = 8bit + # 1 = 16bit + # 2 = 32bit + LSB_FIRST = 13 + b12 = 12 + IE_RX_THRESH = 8 + IE_RX_COMPLETE = 7 + MODE = 6, 5 + # 0 = polled + # 1 = irq + CPOL = 2 + CPHA = 1 + +class R_STATUS(Register32): + TX_COMPLETE = 22 + TXRX_THRESH = 1 # updated if MODE == 1 + RX_COMPLETE = 0 + +class R_PIN(Register32): + CS = 1 + KEEP_MOSI = 0 + +class R_CLKDIV(Register32): + DIVIDER = 10, 0 # SPI freq = CLK / (DIVIDER + 1) + +class R_INTER_DELAY(Register32): + DELAY = 15, 0 + +class R_FIFOSTAT(Register32): + LEVEL_RX = 31, 24 + RX_EMPTY = 20 + LEVEL_TX = 15, 8 + TX_FULL = 4 + +class R_IRQ_XFER(Register32): + TX_XFER_DONE = 1 + RX_XFER_DONE = 0 + +class R_IRQ_FIFO(Register32): + TX_OVERFLOW = 17 + RX_UNDERRUN = 16 + TX_EMPTY = 9 + RX_FULL = 8 + TX_THRESH = 5 + RX_THRESH = 4 + +class R_XFSTATUS(Register32): + SR_FULL = 26 + SHIFTING = 20 + STATE = 17, 16 + UNK = 0 + +class R_DIVSTATUS(Register32): + COUNT2 = 31, 16 + COUNT1 = 15, 0 + +class R_SHIFTCFG(Register32): + OVERRIDE_CS = 24 + BITS = 21, 16 + RX_ENABLE = 11 + TX_ENABLE = 10 + CS_AS_DATA = 9 + AND_CLK_DATA = 8 + #? = 2 # needs to be 1 for RX to not break + CS_ENABLE = 1 + CLK_ENABLE = 0 + +class R_PINCFG(Register32): + MOSI_INIT_VAL = 10 + CS_INIT_VAL = 9 + CLK_INIT_VAL = 8 + KEEP_MOSI = 2 + KEEP_CS = 1 + KEEP_CLK = 0 + +class R_DELAY(Register32): + DELAY = 31, 16 + MOSI_VAL = 12 + CS_VAL = 10 + SCK_VAL = 8 + SET_MOSI = 6 + SET_CS = 5 + SET_SCK = 4 + NO_INTERBYTE = 1 + ENABLE = 0 + +class R_SCKCFG(Register32): + PERIOD = 31, 16 + PHASE1 = 9 + PHASE0 = 8 + RESET_TO_IDLE = 4 + +class R_SCKPHASES(Register32): + PHASE1_START = 31, 16 + PHASE0_START = 15, 0 + +class SPIRegs(RegMap): + CTRL = 0x00, R_CTRL + CFG = 0x04, R_CFG + STATUS = 0x08, R_STATUS + PIN = 0x0C, R_PIN + TXDATA = 0x10, Register32 + RXDATA = 0x20, Register32 + CLKDIV = 0x30, R_CLKDIV + RXCNT = 0x34, Register32 + INTER_DELAY = 0x38, R_INTER_DELAY + TXCNT = 0x4C, Register32 + FIFOSTAT = 0x10C, R_FIFOSTAT + + IE_XFER = 0x130, R_IRQ_XFER + IF_XFER = 0x134, R_IRQ_XFER + IE_FIFO = 0x138, R_IRQ_FIFO + IF_FIFO = 0x13c, R_IRQ_FIFO + + SHIFTCFG = 0x150, R_SHIFTCFG + PINCFG = 0x154, R_PINCFG + + DELAY_PRE = 0x160, R_DELAY + SCKCFG = 0x164, R_SCKCFG + DELAY_POST = 0x168, R_DELAY + + SCKPHASES = 0x180, R_SCKPHASES + + UNK_PHASE = 0x18c, Register32 # probably MISO sample point + + XFSTATUS = 0x1c0, R_XFSTATUS + DIVSTATUS = 0x1e0, R_DIVSTATUS diff --git a/tools/proxyclient/m1n1/hw/spmi.py b/tools/proxyclient/m1n1/hw/spmi.py new file mode 100644 index 0000000..a312f61 --- /dev/null +++ b/tools/proxyclient/m1n1/hw/spmi.py @@ -0,0 +1,98 @@ +# SPDX-License-Identifier: MIT +import struct + +from ..utils import * + +__all__ = ["SPMI"] + +CMD_EXT_WRITE = 0x00 +CMD_EXT_READ = 0x20 +CMD_EXT_WRITEL = 0x30 +CMD_EXT_READL = 0x38 +CMD_WRITE = 0x40 +CMD_READ = 0x60 +CMD_ZERO_WRITE = 0x80 + +class R_CMD(Register32): + REG = 31, 16 + ACTIVE = 15 + SLAVE_ID = 14, 8 + CMD = 7, 0 + +class R_STATUS(Register32): + RX_EMPTY = 24 + RX_COUNT = 23, 16 + TX_EMPTY = 8 + TX_COUNT = 7, 0 + +class SPMIRegs(RegMap): + STATUS = 0x00, R_STATUS + CMD = 0x04, R_CMD + REPLY = 0x08, Register32 + IRQ_FLAG = 0x80, Register32 + +class SPMI: + def __init__(self, u, adt_path): + self.u = u + self.p = u.proxy + self.iface = u.iface + self.base = u.adt[adt_path].get_reg(0)[0] + self.regs = SPMIRegs(u, self.base) + + def read(self, slave, reg, size): + while not self.regs.STATUS.reg.RX_EMPTY: + print(">", self.regs.REPLY.val) + + self.regs.CMD.reg = R_CMD(REG = reg, ACTIVE=1, SLAVE_ID = slave, CMD = CMD_EXT_READL | (size - 1)) + + buf = b"" + + left = size + 4 + while left > 0: + while self.regs.STATUS.reg.RX_EMPTY: + pass + v = self.regs.REPLY.val + buf += struct.pack("<I", v) + left -= 4 + + return buf[4:4+size] + + def write(self, slave, reg, data): + while not self.regs.STATUS.reg.RX_EMPTY: + self.regs.REPLY.val + + size = len(data) + self.regs.CMD.reg = R_CMD(REG = reg, ACTIVE=1, SLAVE_ID = slave, CMD = CMD_EXT_WRITEL | (size - 1)) + + while data: + blk = (data[:4] + b"\0\0\0")[:4] + self.regs.CMD.val = struct.unpack("<I", blk)[0] + data = data[4:] + + while self.regs.STATUS.reg.RX_EMPTY: + pass + return self.regs.REPLY.val + + def read8(self, slave, reg): + return struct.unpack("<B", self.read(slave, reg, 1))[0] + + def read16(self, slave, reg): + return struct.unpack("<H", self.read(slave, reg, 2))[0] + + def read32(self, slave, reg): + return struct.unpack("<I", self.read(slave, reg, 4))[0] + + def read64(self, slave, reg): + return struct.unpack("<Q", self.read(slave, reg, 8))[0] + + def write8(self, slave, reg, val): + return self.write(slave, reg, struct.pack("<B", val)) + + def write16(self, slave, reg, val): + return self.write(slave, reg, struct.pack("<H", val)) + + def write32(self, slave, reg, val): + return self.write(slave, reg, struct.pack("<I", val)) + + def write64(self, slave, reg, val): + return self.write(slave, reg, struct.pack("<Q", val)) diff --git a/tools/proxyclient/m1n1/hw/uat.py b/tools/proxyclient/m1n1/hw/uat.py new file mode 100644 index 0000000..f5dce9f --- /dev/null +++ b/tools/proxyclient/m1n1/hw/uat.py @@ -0,0 +1,571 @@ +""" + UAT is just regular ARMv8 pagetables, shared between the gfx-asc firmware + and the actual AGX hardware. + + The OS doesn't have direct control over it, TTBR0 and TTBR1 entries are placed at + gpu-region-base, one pair for each context. The firmware automatically loads TTBR0/TTBR1 + on boot and whenever the context changes. +""" + + +import struct +from ..fw.agx.handoff import GFXHandoff +from ..utils import * +from ..malloc import Heap +from enum import IntEnum +import traceback + +__all__ = [] + +class MemoryAttr(IntEnum): + # ff = Normal, Outer Writeback RW, Inner Writeback RW + Normal = 0 # Only accessed by the gfx-asc coprocessor + # 00 = Device nGnRnE + Device = 1 + # f4 = Normal, Outer Writeback RW, Inner NC + Shared = 2 # Probally Outer-shareable. Shared with either the main cpu or AGX hardware + # 4f = Normal, Outer NC, Inner Writeback RW + UNK3 = 3 + # 00 = Device nGnRnE + UNK4 = 4 + # ff = Normal, Outer Writeback RW, Inner Writeback RW + UNK5 = 5 + # 00 = Device nGnRnE + UNK6 = 6 + # 00 = Device nGnRnE + UNK7 = 7 + + +class TTBR(Register64): + ASID = 63, 48 + BADDR = 47, 1 + VALID = 0 + + def valid(self): + return self.VALID == 1 + + def offset(self): + return self.BADDR << 1 + + def set_offset(self, offset): + self.BADDR = offset >> 1 + + def describe(self): + return f"{self.offset():x} [ASID={self.ASID}, VALID={self.VALID}]" + +class PTE(Register64): + OFFSET = 47, 14 + UNK0 = 10 # probally an ownership flag, seems to be 1 for FW created PTEs and 0 for OS PTEs + TYPE = 1 + VALID = 0 + + def valid(self): + return self.VALID == 1 and self.TYPE == 1 + + def offset(self): + return self.OFFSET << 14 + + def set_offset(self, offset): + self.OFFSET = offset >> 14 + + def describe(self): + if not self.valid(): + return f"<invalid> [{int(self)}:x]" + return f"{self.offset():x}, UNK={self.UNK0}" + +class Page_PTE(Register64): + OS = 55 # Owned by host os or firmware + UXN = 54 + PXN = 53 + OFFSET = 47, 14 + nG = 11 # global or local TLB caching + AF = 10 + SH = 9, 8 + AP = 7, 6 + AttrIndex = 4, 2 + TYPE = 1 + VALID = 0 + + def valid(self): + return self.VALID == 1 and self.TYPE == 1 + + def offset(self): + return self.OFFSET << 14 + + def set_offset(self, offset): + self.OFFSET = offset >> 14 + + def access_fw(self, gl=False): + if not self.OS: + return [[ + ["--", "--", "--", "--"], + ["--", "RW", "--", "RW"], + ["--", "RX", "--", "--"], + ["RX", "R-", "--", "R-"], + ], [ + ["--", "--", "--", "RW"], + ["--", "--", "--", "RW"], + ["RX", "--", "--", "R-"], + ["RX", "RW", "--", "R-"], + ]][gl][self.AP][(self.UXN << 1) | self.PXN] + else: + return [ + ["--", "R-", "-?", "RW"], + ["R-", "--", "RW", "RW"], + ["--", "--", "--", "--"], + ["--", "--", "--", "--"], + ][self.AP][(self.UXN << 1) | self.PXN] + + def access_gpu(self): + if not self.OS: + return "--" + + return [ + ["--", "R-", "-W", "RW"], + ["--", "--", "--", "R-"], + ["R-", "-W", "RW", "--"], + ["--", "--", "--", "--"], + ][self.AP][(self.UXN << 1) | self.PXN] + + def describe(self): + if not self.valid(): + return f"<invalid> [{int(self)}:x]" + + return ( + f"{self.offset():x} [GPU={self.access_gpu()}, EL1={self.access_fw(0)}, GL1={self.access_fw(1)}, " + + f"perm={self.OS}{self.AP:02b}{self.UXN}{self.PXN}, " + + f"{MemoryAttr(self.AttrIndex).name}, {['Global', 'Local'][self.nG]}, " + + f"Owner={['FW', 'OS'][self.OS]}, AF={self.AF}, SH={self.SH}] ({self.value:#x})" + ) + +class UatAccessor(Reloadable): + def __init__(self, uat, ctx=0): + self.uat = uat + self.ctx = ctx + + def translate(self, addr, width): + paddr, _ = self.uat.iotranslate(self.ctx, addr, width)[0] + if paddr is None: + raise Exception(f"UAT Failed to translate {addr:#x}") + return paddr + + def read(self, addr, width): + return self.uat.u.read(self.translate(addr, width), width) + def read8(self, addr): + return self.uat.p.read8(self.translate(addr, 1)) + def read16(self, addr): + return self.uat.p.read16(self.translate(addr, 2)) + def read32(self, addr): + return self.uat.p.read32(self.translate(addr, 4)) + def read64(self, addr): + return self.uat.p.read64(self.translate(addr, 8)) + + def write(self, addr, data, width): + self.uat.u.write(self.translate(addr, width), data, width) + def write8(self, addr, data): + self.uat.p.write8(self.translate(addr, 1), daat) + def write16(self, addr, data): + self.uat.p.write6(self.translate(addr, 2), data) + def write32(self, addr, data): + self.uat.p.write32(self.translate(addr, 4), data) + def write64(self, addr, data): + self.uat.p.write64(self.translate(addr, 8), data) + +class UatStream(Reloadable): + CACHE_SIZE = 0x1000 + + def __init__(self, uat, ctx, addr, recurse=True): + self.uat = uat + self.ctx = ctx + self.pos = addr + self.cache = None + self.meta_fn = None + self.recurse = recurse + + def to_accessor(self): + return UatAccessor(self.uat, self.ctx) + + def read(self, size): + assert size >= 0 + + data = b"" + if self.cache: + data = self.cache[:size] + cached = len(self.cache) + self.pos += min(cached, size) + if cached > size: + self.cache = self.cache[size:] + return data + self.cache = None + if cached == size: + return data + + size -= cached + + # align any cache overreads to the next page boundary + remaining_in_page = self.uat.PAGE_SIZE - (self.pos % self.uat.PAGE_SIZE) + to_cache = min(remaining_in_page, self.CACHE_SIZE) + + try: + self.cache = self.uat.ioread(self.ctx, self.pos, max(size, to_cache)) + except: + traceback.print_exc() + raise + return data + self.read(size) + + def readable(self): + return True + + def write(self, bytes): + self.uat.iowrite(self.ctx, self.pos, bytes) + self.pos += len(bytes) + self.cache = None + return len(bytes) + + def writable(self): + return True + + def flush(self): + self.cache = None + + def seek(self, n, wherenc=0): + self.cache = None + if wherenc == 0: + self.pos = n + elif wherenc == 2: + self.pos += n + + def seekable(self): + return True + + def tell(self): + return self.pos + + def closed(self): + return False + + +class UAT(Reloadable): + NUM_CONTEXTS = 64 + + PAGE_BITS = 14 + PAGE_SIZE = 1 << PAGE_BITS + + L0_SIZE = 2 + L0_OFF = 39 + L1_SIZE = 8 + L1_OFF = 36 + L2_OFF = 25 + L3_OFF = 14 + + IDX_BITS = 11 + Lx_SIZE = (1 << IDX_BITS) + + LEVELS = [ + (L0_OFF, L0_SIZE, TTBR), + (L1_OFF, L1_SIZE, PTE), + (L2_OFF, Lx_SIZE, PTE), + (L3_OFF, Lx_SIZE, Page_PTE), + ] + + def __init__(self, iface, util=None, hv=None): + self.iface = iface + self.u = util + self.p = util.proxy + self.hv = hv + self.pt_cache = {} + self.dirty = set() + self.dirty_ranges = {} + self.allocator = None + self.ttbr = None + self.initialized = False + self.sgx_dev = self.u.adt["/arm-io/sgx"] + self.shared_region = self.sgx_dev.gfx_shared_region_base + self.gpu_region = self.sgx_dev.gpu_region_base + self.ttbr0_base = self.u.memalign(self.PAGE_SIZE, self.PAGE_SIZE) + self.ttbr1_base = self.sgx_dev.gfx_shared_region_base + self.handoff = GFXHandoff(self.u) + + self.VA_MASK = 0 + for (off, size, _) in self.LEVELS: + self.VA_MASK |= (size - 1) << off + self.VA_MASK |= self.PAGE_SIZE - 1 + + + def set_l0(self, ctx, off, base, asid=0): + ttbr = TTBR(BADDR = base >> 1, ASID = asid, VALID=(base != 0)) + print(f"[UAT] Set L0 ctx={ctx} off={off:#x} base={base:#x} asid={asid} ({ttbr})") + self.write_pte(self.gpu_region + ctx * 16, off, 2, ttbr) + + def ioread(self, ctx, base, size): + if size == 0: + return b"" + + ranges = self.iotranslate(ctx, base, size) + + iova = base + data = [] + for addr, size in ranges: + if addr is None: + raise Exception(f"Unmapped page at iova {ctx}:{iova:#x}") + data.append(self.iface.readmem(addr, size)) + iova += size + + return b"".join(data) + + def iowrite(self, ctx, base, data): + if len(data) == 0: + return + + ranges = self.iotranslate(ctx, base, len(data)) + + iova = base + p = 0 + for addr, size in ranges: + if addr is None: + raise Exception(f"Unmapped page at iova {ctx}:{iova:#x}") + self.iface.writemem(addr, data[p:p + size]) + p += size + iova += size + + # A stream interface that can be used for random access by Construct + def iostream(self, ctx, base, recurse=True): + return UatStream(self, ctx, base, recurse) + + # A read/write register interface like proxy/utils objects that can be used by RegMap + def ioaccessor(self, ctx): + return UatAccessor(self, ctx) + + def iomap(self, ctx, addr, size, **flags): + iova = self.allocator.malloc(size) + + self.iomap_at(ctx, iova, addr, size, **flags) + self.flush_dirty() + return iova + + def iomap_at(self, ctx, iova, addr, size, **flags): + if size == 0: + return + + 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}") + + self.init() + + map_flags = {'OS': 1, 'AttrIndex': MemoryAttr.Normal, 'VALID': 1, 'TYPE': 1, 'AP': 1, 'AF': 1, 'UXN': 1} + map_flags.update(flags) + + start_page = align_down(iova, self.PAGE_SIZE) + end = iova + size + end_page = align_up(end, self.PAGE_SIZE) + + for page in range(start_page, end_page, self.PAGE_SIZE): + table_addr = self.gpu_region + ctx * 16 + for (offset, size, ptecls) in self.LEVELS: + if ptecls is Page_PTE: + pte = Page_PTE(**map_flags) + pte.set_offset(addr) + self.write_pte(table_addr, page >> offset, size, pte) + addr += self.PAGE_SIZE + else: + pte = self.fetch_pte(table_addr, page >> offset, size, ptecls) + if not pte.valid(): + table = self.u.memalign(self.PAGE_SIZE, self.PAGE_SIZE) + self.p.memset32(table, 0, self.PAGE_SIZE) + pte.set_offset(table) + if ptecls is not TTBR: + pte.VALID = 1 + pte.TYPE = 1 + #pte.UNK0 = 1 + self.write_pte(table_addr, page >> offset, size, pte) + table_addr = pte.offset() + + self.dirty_ranges.setdefault(ctx, []).append((start_page, end_page - start_page)) + #self.flush_dirty() + + + def fetch_pte(self, offset, idx, size, ptecls): + idx = idx & (size - 1) + + cached, table = self.get_pt(offset, size=size) + pte = ptecls(table[idx]) + if not pte.valid() and cached: + self.flush_dirty() + cached, table = self.get_pt(offset, size=size, uncached=True) + pte = ptecls(table[idx]) + + return pte + + def write_pte(self, offset, idx, size, pte): + idx = idx & (size - 1) + + cached, table = self.get_pt(offset, size=size) + + table[idx] = pte.value + self.dirty.add(offset) + + def iotranslate(self, ctx, start, size): + if size == 0: + return [] + + start = start & self.VA_MASK + + 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): + table_addr = self.gpu_region + ctx * 16 + for (offset, size, ptecls) in self.LEVELS: + pte = self.fetch_pte(table_addr, page >> offset, size, ptecls) + if not pte.valid(): + break + table_addr = pte.offset() + + if pte.valid(): + pages.append(pte.offset()) + else: + pages.append(None) + + 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, size=None, uncached=False): + if size is None: + size = self.Lx_SIZE + cached = True + if addr not in self.pt_cache or uncached: + cached = False + self.pt_cache[addr] = list( + struct.unpack(f"<{size}Q", self.iface.readmem(addr, size * 8))) + + return cached, self.pt_cache[addr] + + def flush_pt(self, addr): + assert addr in self.pt_cache + table = self.pt_cache[addr] + self.iface.writemem(addr, struct.pack(f"<{len(table)}Q", *table)) + #self.p.dc_civac(addr, 0x4000) + + def flush_dirty(self): + inval = False + + for page in self.dirty: + self.flush_pt(page) + inval = True + + self.dirty.clear() + + for ctx, ranges in self.dirty_ranges.items(): + asid = ctx << 48 + self.u.inst("tlbi aside1os, x0", asid) + + def invalidate_cache(self): + self.pt_cache = {} + + def recurse_level(self, level, base, table, page_fn=None, table_fn=None): + def extend(addr): + if addr >= 0x80_00000000: + addr |= 0xf00_00000000 + return addr + + offset, size, ptecls = self.LEVELS[level] + + cached, tbl = self.get_pt(table, size) + sparse = False + for i, pte in enumerate(tbl): + pte = ptecls(pte) + if not pte.valid(): + sparse = True + continue + + range_size = 1 << offset + start = extend(base + i * range_size) + end = start + range_size - 1 + + if level + 1 == len(self.LEVELS): + if page_fn: + page_fn(start, end, i, pte, level, sparse=sparse) + else: + if table_fn: + table_fn(start, end, i, pte, level, sparse=sparse) + self.recurse_level(level + 1, start, pte.offset(), page_fn, table_fn) + + sparse = False + + def foreach_page(self, ctx, page_fn): + self.recurse_level(0, 0, self.gpu_region + ctx * 16, page_fn) + + def foreach_table(self, ctx, table_fn): + self.recurse_level(0, 0, self.gpu_region + ctx * 16, table_fn=table_fn) + + def init(self): + if self.initialized: + return + + print("[UAT] Initializing...") + + # Clear out any stale kernel page tables + self.p.memset64(self.ttbr1_base + 0x10, 0, 0x3ff0) + self.u.inst("tlbi vmalle1os") + + self.handoff.initialize() + + with self.handoff.lock(): + print(f"[UAT] TTBR0[0] = {self.ttbr0_base:#x}") + print(f"[UAT] TTBR1[0] = {self.ttbr1_base:#x}") + self.set_l0(0, 0, self.ttbr0_base) + self.set_l0(0, 1, self.ttbr1_base) + self.flush_dirty() + self.invalidate_cache() + + print("[UAT] Init complete") + + self.initialized = True + + def bind_context(self, ctx, ttbr0_base): + assert ctx != 0 + + with self.handoff.lock(): + self.set_l0(ctx, 0, ttbr0_base, ctx) + self.set_l0(ctx, 1, self.ttbr1_base, ctx) + self.flush_dirty() + self.invalidate_cache() + + def dump(self, ctx, log=print): + def print_fn(start, end, i, pte, level, sparse): + type = "page" if level+1 == len(self.LEVELS) else "table" + if sparse: + log(f"{' ' * level}...") + log(f"{' ' * level}{type}({i:03}): {start:011x} ... {end:011x}" + f" -> {pte.describe()}") + + self.recurse_level(0, 0, self.gpu_region + ctx * 16, print_fn, print_fn) + +__all__.extend(k for k, v in globals().items() + if (callable(v) or isinstance(v, type)) and v.__module__ == __name__) |
