summaryrefslogtreecommitdiff
path: root/tools/proxyclient/m1n1/fw
diff options
context:
space:
mode:
authormagh <magh@maghmogh.com>2023-03-06 18:44:55 -0600
committermagh <magh@maghmogh.com>2023-03-06 18:44:55 -0600
commite80d9d8871b325a04b18f90a9ea4bb7fd148fb25 (patch)
tree79dbdb8506b7ff1e92549188d1b94cfc0b3503ae /tools/proxyclient/m1n1/fw
add m1n1HEADmaster
Diffstat (limited to 'tools/proxyclient/m1n1/fw')
-rw-r--r--tools/proxyclient/m1n1/fw/__init__.py0
-rw-r--r--tools/proxyclient/m1n1/fw/afk/__init__.py0
-rw-r--r--tools/proxyclient/m1n1/fw/afk/epic.py292
-rw-r--r--tools/proxyclient/m1n1/fw/afk/rbep.py225
-rw-r--r--tools/proxyclient/m1n1/fw/agx/__init__.py98
-rw-r--r--tools/proxyclient/m1n1/fw/agx/channels.py481
-rw-r--r--tools/proxyclient/m1n1/fw/agx/cmdqueue.py516
-rw-r--r--tools/proxyclient/m1n1/fw/agx/handoff.py120
-rw-r--r--tools/proxyclient/m1n1/fw/agx/initdata.py1931
-rw-r--r--tools/proxyclient/m1n1/fw/agx/microsequence.py800
-rw-r--r--tools/proxyclient/m1n1/fw/aop/__init__.py31
-rw-r--r--tools/proxyclient/m1n1/fw/aop/bootargs.py64
-rw-r--r--tools/proxyclient/m1n1/fw/aop/ipc.py365
-rw-r--r--tools/proxyclient/m1n1/fw/asc/__init__.py125
-rw-r--r--tools/proxyclient/m1n1/fw/asc/base.py59
-rw-r--r--tools/proxyclient/m1n1/fw/asc/crash.py248
-rw-r--r--tools/proxyclient/m1n1/fw/asc/ioreporting.py56
-rw-r--r--tools/proxyclient/m1n1/fw/asc/kdebug.py57
-rw-r--r--tools/proxyclient/m1n1/fw/asc/mgmt.py144
-rw-r--r--tools/proxyclient/m1n1/fw/asc/oslog.py30
-rw-r--r--tools/proxyclient/m1n1/fw/asc/syslog.py72
-rw-r--r--tools/proxyclient/m1n1/fw/common.py170
-rw-r--r--tools/proxyclient/m1n1/fw/dcp/__init__.py2
-rw-r--r--tools/proxyclient/m1n1/fw/dcp/client.py14
-rw-r--r--tools/proxyclient/m1n1/fw/dcp/dcpav.py110
-rw-r--r--tools/proxyclient/m1n1/fw/dcp/dcpep.py163
-rw-r--r--tools/proxyclient/m1n1/fw/dcp/iboot.py215
-rw-r--r--tools/proxyclient/m1n1/fw/dcp/ipc.py789
-rw-r--r--tools/proxyclient/m1n1/fw/dcp/manager.py319
-rw-r--r--tools/proxyclient/m1n1/fw/dcp/parse_log.py43
-rw-r--r--tools/proxyclient/m1n1/fw/mtp.py411
-rw-r--r--tools/proxyclient/m1n1/fw/pmp.py168
-rw-r--r--tools/proxyclient/m1n1/fw/smc.py202
33 files changed, 8320 insertions, 0 deletions
diff --git a/tools/proxyclient/m1n1/fw/__init__.py b/tools/proxyclient/m1n1/fw/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/__init__.py
diff --git a/tools/proxyclient/m1n1/fw/afk/__init__.py b/tools/proxyclient/m1n1/fw/afk/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/afk/__init__.py
diff --git a/tools/proxyclient/m1n1/fw/afk/epic.py b/tools/proxyclient/m1n1/fw/afk/epic.py
new file mode 100644
index 0000000..e95281d
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/afk/epic.py
@@ -0,0 +1,292 @@
+# SPDX-License-Identifier: MIT
+
+import struct
+from io import BytesIO
+from construct import *
+from ..common import *
+from ...utils import *
+from ..asc import StandardASC
+from ..asc.base import *
+from .rbep import AFKRingBufEndpoint
+
+EPICType = "EPICType" / Enum(Int32ul,
+ NOTIFY = 0,
+ COMMAND = 3,
+ REPLY = 4,
+ NOTIFY_ACK = 8,
+)
+
+EPICCategory = "EPICCategory" / Enum(Int8ul,
+ REPORT = 0x00,
+ NOTIFY = 0x10,
+ REPLY = 0x20,
+ COMMAND = 0x30,
+)
+
+EPICHeader = Struct(
+ "channel" / Int32ul,
+ "type" / EPICType,
+ "version" / Const(2, Int8ul),
+ "seq" / Int16ul,
+ "pad" / Const(0, Int8ul),
+ "unk" / Const(0, Int32ul),
+ "timestamp" / Default(Int64ul, 0),
+)
+
+EPICSubHeader = Struct(
+ "length" / Int32ul,
+ "version" / Default(Int8ul, 4),
+ "category" / EPICCategory,
+ "type" / Hex(Int16ul),
+ "timestamp" / Default(Int64ul, 0),
+ "seq" / Int16ul,
+ "unk" / Default(Hex(Int16ul), 0),
+ "inline_len" / Hex(Int32ul),
+)
+
+EPICAnnounce = Struct(
+ "name" / Padded(32, CString("utf8")),
+ "props" / Optional(OSSerialize())
+)
+
+EPICSetProp = Struct(
+ "name_len" / Int32ul,
+ "name" / Aligned(4, CString("utf8")),
+ "value" / OSSerialize()
+)
+
+EPICCmd = Struct(
+ "retcode" / Default(Hex(Int32ul), 0),
+ "rxbuf" / Hex(Int64ul),
+ "txbuf" / Hex(Int64ul),
+ "rxlen" / Hex(Int32ul),
+ "txlen" / Hex(Int32ul),
+ "rxcookie" / Optional(Default(Bool(Int8ul), False)),
+ "txcookie" / Optional(Default(Bool(Int8ul), False)),
+)
+
+
+class EPICError(Exception):
+ pass
+
+
+class EPICService:
+ RX_BUFSIZE = 0x4000
+ TX_BUFSIZE = 0x4000
+
+ def __init__(self, ep):
+ self.iface = ep.asc.iface
+ self.ep = ep
+ self.ready = False
+ self.chan = None
+ self.seq = 0
+
+ def log(self, msg):
+ print(f"[{self.ep.name}.{self.SHORT}] {msg}")
+
+ def init(self, props):
+ self.log(f"Init: {props}")
+ self.props = props
+ self.rxbuf, self.rxbuf_dva = self.ep.asc.ioalloc(self.RX_BUFSIZE)
+ self.txbuf, self.txbuf_dva = self.ep.asc.ioalloc(self.TX_BUFSIZE)
+ self.ready = True
+
+ def wait(self):
+ while not self.ready:
+ self.ep.asc.work()
+
+ def handle_report(self, category, type, seq, fd):
+ self.log(f"Report {category}/{type} #{seq}")
+ chexdump(fd.read())
+
+ def handle_notify(self, category, type, seq, fd):
+ retcode = struct.unpack("<I", fd.read(4))[0]
+ self.log(f"Notify {category}/{type} #{seq} ({retcode})")
+ data = fd.read()
+ chexdump(data)
+ print("Send ACK")
+
+ data = data[:0x50] + b"\x01\x00\x00\x00" + data[0x54:]
+
+ pkt = struct.pack("<I", 0) + data
+ self.ep.send_epic(self.chan, EPICType.NOTIFY_ACK, EPICCategory.REPLY, type, seq, pkt, len(data))
+
+ def handle_reply(self, category, type, seq, fd):
+ off = fd.tell()
+ data = fd.read()
+ if len(data) == 4:
+ retcode = struct.unpack("<I", data)[0]
+ if retcode:
+ raise EPICError(f"IOP returned errcode {retcode:#x}")
+ else:
+ self.reply = retcode
+ return
+ fd.seek(off)
+ cmd = EPICCmd.parse_stream(fd)
+ payload = fd.read()
+ self.log(f"Response {type:#x} #{seq}: {cmd.retcode:#x}")
+ if cmd.retcode != 0:
+ raise EPICError(f"IOP returned errcode {cmd.retcode:#x}")
+ if payload:
+ self.log("Inline payload:")
+ chexdump(payload)
+ assert cmd.rxbuf == self.rxbuf_dva
+ self.reply = self.iface.readmem(self.rxbuf, cmd.rxlen)
+
+ def handle_cmd(self, category, type, seq, fd):
+ cmd = EPICCmd.parse_stream(fd)
+ self.log(f"Command {type:#x} #{seq}: {cmd.retcode:#x}")
+
+ def send_cmd(self, type, data, retlen=None):
+ if retlen is None:
+ retlen = len(data)
+ cmd = Container()
+ cmd.rxbuf = self.rxbuf_dva
+ cmd.txbuf = self.txbuf_dva
+ cmd.txlen = len(data)
+ cmd.rxlen = retlen
+ self.iface.writemem(self.txbuf, data)
+ self.reply = None
+ pkt = EPICCmd.build(cmd)
+ self.ep.send_epic(self.chan, EPICType.COMMAND, EPICCategory.COMMAND, type, self.seq, pkt)
+ self.seq += 1
+ while self.reply is None:
+ self.ep.asc.work()
+ return self.reply
+
+class EPICStandardService(EPICService):
+ def call(self, group, cmd, data=b'', replen=None):
+ msg = struct.pack("<2xHIII48x", group, cmd, len(data), 0x69706378) + data
+ if replen is not None:
+ replen += 64
+ resp = self.send_cmd(0xc0, msg, replen)
+ if not resp:
+ return
+ rgroup, rcmd, rlen, rmagic = struct.unpack("<2xHIII", resp[:16])
+ assert rmagic == 0x69706378
+ assert rgroup == group
+ assert rcmd == cmd
+ return resp[64:64+rlen]
+
+ def getLocation(self, unk=0):
+ return struct.unpack("<16xI12x", self.call(4, 4, bytes(32)))
+
+ def getUnit(self, unk=0):
+ return struct.unpack("<16xI12x", self.call(4, 5, bytes(32)))
+
+ def open(self, unk=0):
+ self.call(4, 6, struct.pack("<16xI12x", unk))
+
+ def close(self):
+ self.call(4, 7, bytes(16))
+
+class AFKSystemService(EPICService):
+ NAME = "system"
+ SHORT = "system"
+
+ def getProperty(self, prop, val):
+ pass
+ #self.send_cmd(0x40, msg, 0)
+
+ def setProperty(self, prop, val):
+ msg = {
+ "name_len": (len(prop) + 3) & ~3,
+ "name": prop,
+ "value": val,
+ }
+ msg = EPICSetProp.build(msg)
+ self.send_cmd(0x43, msg, 0)
+
+class EPICEndpoint(AFKRingBufEndpoint):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.serv_map = {}
+ self.chan_map = {}
+ self.serv_names = {}
+ self.hseq = 0
+
+ for i in self.SERVICES:
+ self.serv_names[i.NAME] = i
+
+ def handle_ipc(self, data):
+ fd = BytesIO(data)
+ hdr = EPICHeader.parse_stream(fd)
+ sub = EPICSubHeader.parse_stream(fd)
+
+ if self.verbose > 2:
+ self.log(f"Ch {hdr.channel} Type {hdr.type} Ver {hdr.version} Seq {hdr.seq}")
+ self.log(f" Len {sub.length} Ver {sub.version} Cat {sub.category} Type {sub.type:#x} Seq {sub.seq}")
+
+ if sub.category == EPICCategory.REPORT:
+ self.handle_report(hdr, sub, fd)
+ if sub.category == EPICCategory.NOTIFY:
+ self.handle_notify(hdr, sub, fd)
+ elif sub.category == EPICCategory.REPLY:
+ self.handle_reply(hdr, sub, fd)
+ elif sub.category == EPICCategory.COMMAND:
+ self.handle_cmd(hdr, sub, fd)
+
+ def wait_for(self, name):
+ while True:
+ srv = getattr(self, name, None)
+ if srv is not None and srv.ready:
+ break
+ self.asc.work()
+
+ def handle_report(self, hdr, sub, fd):
+ if sub.type == 0x30:
+ init = EPICAnnounce.parse_stream(fd)
+ if init.props is None:
+ init.props = {}
+ name = init.name
+ if "EPICName" in init.props:
+ name = init.props["EPICName"]
+ key = name + str(init.props.get("EPICUnit", ""))
+ if name in self.serv_names:
+ srv = self.serv_names[name](self)
+ short = srv.SHORT + str(init.props.get("EPICUnit", ""))
+ setattr(self, short, srv)
+ srv.init(init.props)
+ srv.chan = hdr.channel
+ self.chan_map[hdr.channel] = srv
+ self.serv_map[key] = srv
+ self.log(f"New service: {key} on channel {hdr.channel} (short name: {short})")
+ else:
+ self.log(f"Unknown service {key} on channel {hdr.channel}")
+ else:
+ if hdr.channel not in self.chan_map:
+ self.log(f"Ignoring report on channel {hdr.channel}")
+ else:
+ self.chan_map[hdr.channel].handle_report(sub.category, sub.type, sub.seq, fd)
+
+ def handle_notify(self, hdr, sub, fd):
+ self.chan_map[hdr.channel].handle_notify(sub.category, sub.type, sub.seq, fd)
+
+ def handle_reply(self, hdr, sub, fd):
+ self.chan_map[hdr.channel].handle_reply(sub.category, sub.type, sub.seq, fd)
+
+ def handle_cmd(self, hdr, sub, fd):
+ self.chan_map[hdr.channel].handle_cmd(sub.category, sub.type, sub.seq, fd)
+
+ def send_epic(self, chan, ptype, category, type, seq, data, inline_len=0):
+ hdr = Container()
+ hdr.channel = chan
+ hdr.type = ptype
+ hdr.seq = self.hseq
+ self.hseq += 1
+
+ sub = Container()
+ sub.length = len(data)
+ sub.category = category
+ sub.type = type
+ sub.seq = seq
+ sub.inline_len = inline_len
+ pkt = EPICHeader.build(hdr) + EPICSubHeader.build(sub) + data
+ super().send_ipc(pkt)
+
+class AFKSystemEndpoint(EPICEndpoint):
+ SHORT = "system"
+
+ SERVICES = [
+ AFKSystemService,
+ ]
diff --git a/tools/proxyclient/m1n1/fw/afk/rbep.py b/tools/proxyclient/m1n1/fw/afk/rbep.py
new file mode 100644
index 0000000..872d75f
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/afk/rbep.py
@@ -0,0 +1,225 @@
+# SPDX-License-Identifier: MIT
+
+import struct
+
+from ..common import *
+from ...utils import *
+from ..asc.base import *
+
+
+class AFKEPMessage(Register64):
+ TYPE = 63, 48
+
+class AFKEP_GetBuf(AFKEPMessage):
+ TYPE = 63, 48, Constant(0x89)
+ SIZE = 31, 16
+ TAG = 15, 0
+
+class AFKEP_GetBuf_Ack(AFKEPMessage):
+ TYPE = 63, 48, Constant(0xa1)
+ DVA = 47, 0
+
+class AFKEP_InitRB(AFKEPMessage):
+ OFFSET = 47, 32
+ SIZE = 31, 16
+ TAG = 15, 0
+
+class AFKEP_Send(AFKEPMessage):
+ TYPE = 63, 48, Constant(0xa2)
+ WPTR = 31, 0
+
+class AFKEP_Recv(AFKEPMessage):
+ TYPE = 63, 48, Constant(0x85)
+ WPTR = 31, 0
+
+class AFKEP_Init(AFKEPMessage):
+ TYPE = 63, 48, Constant(0x80)
+
+class AFKEP_Init_Ack(AFKEPMessage):
+ TYPE = 63, 48, Constant(0xa0)
+
+class AFKEP_Start(AFKEPMessage):
+ TYPE = 63, 48, Constant(0xa3)
+
+class AFKEP_Start_Ack(AFKEPMessage):
+ TYPE = 63, 48, Constant(0x86)
+
+class AFKEP_Shutdown(AFKEPMessage):
+ TYPE = 63, 48, Constant(0xc0)
+
+class AFKEP_Shutdown_Ack(AFKEPMessage):
+ TYPE = 63, 48, Constant(0xc1)
+
+
+class AFKError(Exception):
+ pass
+
+class AFKRingBuf(Reloadable):
+ BLOCK_SIZE = 0x40
+
+ def __init__(self, ep, base, size):
+ self.ep = ep
+ self.base = base
+
+ bs, unk = struct.unpack("<II", self.read_buf(0, 8))
+ assert (bs + 3 * self.BLOCK_SIZE) == size
+ self.bufsize = bs
+ self.rptr = 0
+ self.wptr = 0
+
+ def read_buf(self, off, size):
+ return self.ep.iface.readmem(self.base + off, size)
+
+ def write_buf(self, off, data):
+ return self.ep.iface.writemem(self.base + off, data)
+
+ def get_rptr(self):
+ return self.ep.asc.p.read32(self.base + self.BLOCK_SIZE)
+
+ def get_wptr(self):
+ return self.ep.asc.p.read32(self.base + 2 * self.BLOCK_SIZE)
+
+ def update_rptr(self, rptr):
+ self.ep.asc.p.write32(self.base + self.BLOCK_SIZE, self.rptr)
+
+ def update_wptr(self, rptr):
+ self.ep.asc.p.write32(self.base + 2 * self.BLOCK_SIZE, self.wptr)
+
+ def read(self):
+ self.wptr = self.get_wptr()
+
+ while self.wptr != self.rptr:
+ hdr = self.read_buf(3 * self.BLOCK_SIZE + self.rptr, 16)
+ self.rptr += 16
+ magic, size = struct.unpack("<4sI", hdr[:8])
+ assert magic in [b"IOP ", b"AOP "]
+ if size > (self.bufsize - self.rptr):
+ hdr = self.read_buf(3 * self.BLOCK_SIZE, 16)
+ self.rptr = 16
+ magic, size = struct.unpack("<4sI", hdr[:8])
+ assert magic in [b"IOP ", b"AOP "]
+
+ payload = self.read_buf(3 * self.BLOCK_SIZE + self.rptr, size)
+ self.rptr = (align_up(self.rptr + size, self.BLOCK_SIZE)) % self.bufsize
+ self.update_rptr(self.rptr)
+ yield hdr[8:] + payload
+ self.wptr = self.get_wptr()
+
+ self.update_rptr(self.rptr)
+
+ def write(self, data):
+ hdr2, data = data[:8], data[8:]
+ self.rptr = self.get_rptr()
+
+ if self.wptr < self.rptr and self.wptr + 0x10 >= self.rptr:
+ raise AFKError("Ring buffer is full")
+
+ hdr = struct.pack("<4sI", b"IOP ", len(data)) + hdr2
+ self.write_buf(3 * self.BLOCK_SIZE + self.wptr, hdr)
+
+ if len(data) > (self.bufsize - self.wptr - 16):
+ if self.rptr < 0x10:
+ raise AFKError("Ring buffer is full")
+ self.write_buf(3 * self.BLOCK_SIZE, hdr)
+ self.wptr = 0
+
+ if self.wptr < self.rptr and self.wptr + 0x10 + len(data) >= self.rptr:
+ raise AFKError("Ring buffer is full")
+
+ self.write_buf(3 * self.BLOCK_SIZE + self.wptr + 0x10, data)
+ self.wptr = align_up(self.wptr + 0x10 + len(data), self.BLOCK_SIZE) % self.bufsize
+
+ self.update_wptr(self.wptr)
+ return self.wptr
+
+class AFKRingBufEndpoint(ASCBaseEndpoint):
+ BASE_MESSAGE = AFKEPMessage
+ SHORT = "afkep"
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.txq = None
+ self.rxq = None
+ self.iface = self.asc.iface
+ self.alive = False
+ self.started = False
+ self.iobuffer = None
+ self.verbose = 2
+ self.msgid = 0
+
+ def start(self):
+ self.send(AFKEP_Init())
+
+ @msg_handler(0xa0, AFKEP_Init_Ack)
+ def Init_Ack(self, msg):
+ self.alive = True
+ return True
+
+ @msg_handler(0x89, AFKEP_GetBuf)
+ def GetBuf(self, msg):
+ size = msg.SIZE * AFKRingBuf.BLOCK_SIZE
+
+ if self.iobuffer:
+ print("WARNING: trying to reset iobuffer!")
+
+ self.iobuffer, self.iobuffer_dva = self.asc.ioalloc(size)
+ self.asc.p.write32(self.iobuffer, 0xdeadbeef)
+ self.send(AFKEP_GetBuf_Ack(DVA=self.iobuffer_dva))
+ self.log(f"Buffer: phys={self.iobuffer:#x} dva={self.iobuffer_dva:#x} size={size:#x}")
+ return True
+
+ def stop(self):
+ self.log("Shutting down")
+ self.send(AFKEP_Shutdown())
+ while self.alive:
+ self.asc.work()
+
+ @msg_handler(0xc1, AFKEP_Shutdown_Ack)
+ def Shutdown_Ack(self, msg):
+ self.alive = False
+ self.log("Shutdown ACKed")
+ return True
+
+ @msg_handler(0x8a, AFKEP_InitRB)
+ def InitTX(self, msg):
+ self.txq = self.init_rb(msg)
+ if self.rxq and self.txq:
+ self.start_queues()
+ return True
+
+ @msg_handler(0x8b, AFKEP_InitRB)
+ def InitRX(self, msg):
+ self.rxq = self.init_rb(msg)
+ if self.rxq and self.txq:
+ self.start_queues()
+ return True
+
+ def init_rb(self, msg):
+ off = msg.OFFSET * AFKRingBuf.BLOCK_SIZE
+ size = msg.SIZE * AFKRingBuf.BLOCK_SIZE
+
+ return AFKRingBuf(self, self.iobuffer + off, size)
+
+ def start_queues(self):
+ self.send(AFKEP_Start())
+
+ @msg_handler(0x86, AFKEP_Start_Ack)
+ def Start_Ack(self, msg):
+ self.started = True
+ return True
+
+ @msg_handler(0x85, AFKEP_Recv)
+ def Recv(self, msg):
+ for data in self.rxq.read():
+ if self.verbose >= 3:
+ self.log(f"<RX rptr={self.rxq.rptr:#x}")
+ chexdump(data)
+ self.handle_ipc(data)
+ return True
+
+ def handle_ipc(self, data):
+ pass
+
+ def send_ipc(self, data):
+ wptr = self.txq.write(data)
+ self.send(AFKEP_Send(WPTR = wptr))
diff --git a/tools/proxyclient/m1n1/fw/agx/__init__.py b/tools/proxyclient/m1n1/fw/agx/__init__.py
new file mode 100644
index 0000000..e436720
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/agx/__init__.py
@@ -0,0 +1,98 @@
+# SPDX-License-Identifier: MIT
+from ...utils import *
+from ...malloc import Heap
+
+from ..asc import StandardASC
+from ..asc.base import ASCBaseEndpoint, msg_handler
+
+from .initdata import InitData, IOMapping
+
+__all__ = []
+
+class GpuMsg(Register64):
+ TYPE = 63, 48
+
+class InitMsg(GpuMsg):
+ TYPE = 63, 48, Constant(0x81)
+ UNK = 47, 44, Constant(0)
+ INITDATA = 43, 0
+
+class EventMsg(GpuMsg):
+ TYPE = 63, 48, Constant(0x42)
+ UNK = 47, 0, Constant(0)
+
+class DoorbellMsg(GpuMsg):
+ TYPE = 63, 48, Constant(0x83)
+ CHANNEL = 15, 0
+
+class FWCtlMsg(GpuMsg):
+ TYPE = 63, 48, Constant(0x84)
+
+class HaltMsg(GpuMsg):
+ TYPE = 63, 48, Constant(0x85)
+
+class FirmwareEP(ASCBaseEndpoint):
+ BASE_MESSAGE = GpuMsg
+ SHORT = "fw"
+
+ @msg_handler(0x42)
+ def event(self, msg):
+ #self.log("Received event")
+ self.asc.agx.poll_channels()
+ return True
+
+ def send_initdata(self, addr):
+ self.log(f"Sending initdata @ {addr:#x}")
+ msg = InitMsg(INITDATA=addr)
+ self.send(msg)
+
+class DoorbellEP(ASCBaseEndpoint):
+ BASE_MESSAGE = DoorbellMsg
+ SHORT = "db"
+
+ def doorbell(self, channel):
+ #self.log(f"Sending doorbell ch={channel}")
+ msg = DoorbellMsg(CHANNEL = channel)
+ self.send(msg)
+
+ def fwctl_doorbell(self):
+ msg = FWCtlMsg()
+ self.send(msg)
+
+class AGXASC(StandardASC):
+ ENDPOINTS = {
+ 0x20: FirmwareEP,
+ 0x21: DoorbellEP,
+ }
+
+ def __init__(self, u, base, agx, uat):
+ super().__init__(u, base)
+ self.agx = agx
+ self.uat = uat
+
+ def addr(self, addr):
+ base, obj = self.agx.find_object(addr)
+ if base is None:
+ return super().addr(addr)
+
+ return f"{addr:#x} ({obj._name} [{obj._size:#x}] @ {base:#x} + {addr - base:#x})"
+
+ def iomap(self, addr, size):
+ return self.uat.iomap(0, addr, size)
+
+ def ioalloc(self, size):
+ paddr = self.u.memalign(0x4000, size)
+ dva = self.iomap(paddr, size)
+ return paddr, dva
+
+ def ioread(self, dva, size, ctx=0):
+ return self.uat.ioread(ctx, dva & 0xFFFFFFFFFF, size)
+
+ def iowrite(self, dva, data, ctx=0):
+ return self.uat.iowrite(ctx, dva & 0xFFFFFFFFFF, data)
+
+ def iotranslate(self, dva, size, ctx=0):
+ return self.uat.iotranslate(ctx, dva & 0xFFFFFFFFFF, size)
+
+__all__.extend(k for k, v in globals().items()
+ if (callable(v) or isinstance(v, type)) and v.__module__ == __name__)
diff --git a/tools/proxyclient/m1n1/fw/agx/channels.py b/tools/proxyclient/m1n1/fw/agx/channels.py
new file mode 100644
index 0000000..6704d20
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/agx/channels.py
@@ -0,0 +1,481 @@
+
+import random
+
+from m1n1.utils import *
+from m1n1.constructutils import *
+from construct import *
+from .cmdqueue import *
+
+__all__ = ["channelNames", "channelRings", "DeviceControlMsg", "EventMsg", "StatsMsg"]
+
+class RunCmdQueueMsg(ConstructClass):
+ subcon = Struct (
+ "queue_type" / Default(Int32ul, 0),
+ "cmdqueue_addr" / Default(Hex(Int64ul), 0),
+ "cmdqueue" / Lazy(ROPointer(this.cmdqueue_addr, CommandQueueInfo)),
+ "head" / Default(Int32ul, 0),
+ "event_number" / Default(Int32ul, 0),
+ "new_queue" / Default(Int32ul, 0),
+ "data" / HexDump(Default(Bytes(0x18), bytes(0x18))),
+ )
+
+ TYPES = {
+ 0: "SubmitTA",
+ 1: "Submit3D",
+ 2: "SubmitCompute",
+ }
+
+ def __str__(self, *args, **kwargs):
+ s = super().__str__(*args, **kwargs) + "\n"
+
+ if self.cmdqueue_addr == 0:
+ return s + "<Empty RunCmdQueueMsg>"
+
+ r = random.randrange(2**64)
+ s += f"{self.TYPES[self.queue_type]}(0x{self.cmdqueue_addr & 0xfff_ffffffff:x}, {self.head}, ev={self.event_number}, new={self.new_queue}) //{r:x}"
+ return s
+
+class DC_DestroyContext(ConstructClass):
+ subcon = Struct (
+ "msg_type" / Const(0x17, Int32ul),
+ "unk_4" / Hex(Int32ul),
+ "unk_8" / Hex(Int32ul),
+ "unk_c" / Hex(Int32ul),
+ "unk_10" / Hex(Int32ul),
+ "unk_14" / Hex(Int32ul),
+ "unk_18" / Hex(Int32ul),
+ "context_addr" / Hex(Int64ul),
+ "rest" / HexDump(Default(Bytes(0xc), bytes(0xc)))
+ )
+
+class DC_Write32(ConstructClass):
+ subcon = Struct (
+ "msg_type" / Const(0x18, Int32ul),
+ "addr" / Hex(Int64ul),
+ "data" / Int32ul,
+ "unk_10" / Int32ul,
+ "unk_14" / Int32ul,
+ "unk_18" / Int32ul,
+ "unk_1c" / Int32ul,
+ "rest" / HexDump(Default(Bytes(0x10), bytes(0x10)))
+ )
+
+class DC_Write32B(ConstructClass):
+ subcon = Struct (
+ "msg_type" / Const(0x13, Int32ul),
+ "addr" / Hex(Int64ul),
+ "data" / Int32ul,
+ "unk_10" / Int32ul,
+ "unk_14" / Int32ul,
+ "unk_18" / Int32ul,
+ "unk_1c" / Int32ul,
+ "rest" / HexDump(Default(Bytes(0x10), bytes(0x10)))
+ )
+
+class DC_Init(ConstructClass):
+ subcon = Struct (
+ "msg_type" / Const(0x19, Int32ul),
+ "data" / HexDump(Default(Bytes(0x2c), bytes(0x2c)))
+ )
+
+class DC_09(ConstructClass):
+ subcon = Struct (
+ "msg_type" / Const(0x9, Int32ul),
+ "unk_4" / Int64ul,
+ "unkptr_c" / Int64ul,
+ "unk_14" / Int64ul,
+ "data" / HexDump(Default(Bytes(0x14), bytes(0x14)))
+ )
+
+class DC_Any(ConstructClass):
+ subcon = Struct (
+ "msg_type" / Int32ul,
+ "data" / HexDump(Default(Bytes(0x2c), bytes(0x2c)))
+ )
+
+class DC_1e(ConstructClass):
+ subcon = Struct (
+ "msg_type" / Const(0x1e, Int32ul),
+ "unk_4" / Int64ul,
+ "unk_c" / Int64ul,
+ "data" / HexDump(Default(Bytes(0x1c), bytes(0x1c)))
+ )
+
+class DC_UpdateIdleTS(ConstructClass):
+ subcon = Struct (
+ "msg_type" / Const(0x23, Int32ul),
+ "data" / HexDump(Default(Bytes(0x2c), bytes(0x2c))),
+ )
+
+class UnknownMsg(ConstructClass):
+ subcon = Struct (
+ "msg_type" / Hex(Int32ul),
+ "data" / HexDump(Bytes(0x2c)),
+ )
+
+DeviceControlMsg = FixedSized(0x30, Select(
+ DC_DestroyContext,
+ DC_Init,
+ DC_UpdateIdleTS,
+ DC_1e,
+ DC_Write32,
+ UnknownMsg,
+))
+
+class StatsMsg_Power(ConstructClass):
+ subcon = Struct (
+ "msg_type" / Hex(Const(0x00, Int32ul)),
+ ZPadding(0x18), # ??? why the hole? never written...
+ "power" / Hex(Int64ul),
+ ZPadding(0xc), # Confirmed padding
+ )
+
+ def __str__(self):
+ return f"Power: {self.power / 8192.0:.3f} mW"
+
+class StatsMsg_PowerOn(ConstructClass):
+ subcon = Struct (
+ "msg_type" / Hex(Const(0x02, Int32ul)),
+ "power_off_ticks" / Dec(Int64ul),
+ ZPadding(0x24), # Confirmed padding
+ )
+ def __str__(self):
+ t = self.power_off_ticks / 24000000
+ return f"Power ON: spent {t:.04}s powered off ({self.power_off_ticks} ticks)"
+
+class StatsMsg_PowerOff(ConstructClass):
+ subcon = Struct (
+ "msg_type" / Hex(Const(0x03, Int32ul)),
+ "power_on_ticks" / Dec(Int64ul),
+ ZPadding(0x24), # Confirmed padding
+ )
+ def __str__(self):
+ t = self.power_on_ticks / 24000000
+ return f"Power OFF: spent {t:.04}s powered on ({self.power_on_ticks} ticks)"
+
+class StatsMsg_Util(ConstructClass):
+ subcon = Struct (
+ "msg_type" / Hex(Const(0x04, Int32ul)),
+ "timestamp" / Hex(Int64ul),
+ "util1" / Dec(Int32ul),
+ "util2" / Dec(Int32ul),
+ "util3" / Dec(Int32ul),
+ "util4" / Dec(Int32ul),
+ ZPadding(0x14), # Confirmed padding
+ )
+ def __str__(self):
+ return f"Utilization: {self.util1:>3d}% {self.util2:>3d}% {self.util3:>3d}% {self.util4:>3d}%"
+
+class StatsMsg_AvgPower(ConstructClass):
+ subcon = Struct (
+ "msg_type" / Hex(Const(0x09, Int32ul)),
+ "active_cs" / Dec(Int64ul),
+ "unk2" / Hex(Int32ul),
+ "unk3" / Hex(Int32ul),
+ "unk4" / Hex(Int32ul),
+ "avg_power" / Dec(Int32ul),
+ ZPadding(0x14), # Confirmed padding
+ )
+
+ def __str__(self):
+ return f"Activity: Active {self.active_cs * 10:6d} ms Avg Pwr {self.avg_power:4d} mW ({self.unk2:d} {self.unk3:d} {self.unk4:d})"
+
+class StatsMsg_Temp(ConstructClass):
+ subcon = Struct (
+ "msg_type" / Hex(Const(0x0a, Int32ul)),
+ ZPadding(8), # Not written
+ "raw_value" / Hex(Int32ul),
+ "scale" / Hex(Int32ul),
+ "tmin" / Hex(Int32ul),
+ "tmax" / Hex(Int32ul),
+ ZPadding(0x14), # Confirmed padding
+ )
+
+ def __str__(self):
+ temp = self.raw_value / float(self.scale) / 64.0
+ return f"Temp: {temp:.2f}°C s={self.scale:d} tmin={self.tmin:d} tmax={self.tmax:d}"
+
+class StatsMsg_PowerState(ConstructClass):
+ subcon = Struct (
+ "msg_type" / Hex(Const(0x0b, Int32ul)),
+ "timestamp" / Hex(Int64ul),
+ "last_busy_ts" / Hex(Int64ul),
+ "active" / Hex(Int32ul),
+ "poweroff" / Dec(Int32ul),
+ "unk2" / Dec(Int32ul),
+ "pstate" / Dec(Int32ul),
+ "unk4" / Dec(Int32ul),
+ "unk5" / Dec(Int32ul),
+ ZPadding(4), # Confirmed padding
+ )
+
+ def __str__(self):
+ act = "ACT" if self.active else " "
+ off = "OFF" if self.poweroff else " "
+
+ return f"PowerState: {act} {off} ps={int(self.pstate)} {self.unk4} {self.unk2} {self.unk5}"
+
+class StatsMsg_FWBusy(ConstructClass):
+ subcon = Struct (
+ "msg_type" / Hex(Const(0x0c, Int32ul)),
+ "timestamp" / Hex(Int64ul),
+ "flag" / Int32ul,
+ ZPadding(0x20), # Confirmed padding
+ )
+
+ def __str__(self):
+ return f"FW active: {bool(self.flag)}"
+
+class StatsMsg_PState(ConstructClass):
+ subcon = Struct (
+ "msg_type" / Hex(Const(0x0d, Int32ul)),
+ ZPadding(8), # Not written
+ "ps_min" / Dec(Int32ul),
+ "unk1" / Dec(Int32ul),
+ "ps_max" / Dec(Int32ul),
+ "unk3" / Dec(Int32ul),
+ ZPadding(0x14), # Confirmed padding
+ )
+ def __str__(self):
+ return f"PState: {self.ps_min:d}..{self.ps_max:d} ({self.unk1:d}/{self.unk3:d})"
+
+class StatsMsg_TempSensor(ConstructClass):
+ subcon = Struct (
+ "msg_type" / Hex(Const(0x0e, Int32ul)),
+ ZPadding(4), # Not written
+ "sensor_id" / Hex(Int32ul),
+ "raw_value" / Hex(Int32ul),
+ "scale" / Dec(Int32ul),
+ "tmin" / Dec(Int32ul),
+ "tmax" / Dec(Int32ul),
+ ZPadding(0x14), # Confirmed padding
+ )
+ def __str__(self):
+ temp = self.raw_value / float(self.scale) / 64.0
+ return f"TempSensor: #{self.sensor_id:d} {temp:.2f}°C s={self.scale:d} tmin={self.tmin:d} tmax={self.tmax:d}"
+
+StatsMsg = FixedSized(0x30, Select(
+ StatsMsg_Power,
+ StatsMsg_PowerOn,
+ StatsMsg_PowerOff,
+ StatsMsg_Util,
+ StatsMsg_AvgPower,
+ StatsMsg_Temp,
+ StatsMsg_PowerState,
+ StatsMsg_FWBusy,
+ StatsMsg_PState,
+ StatsMsg_TempSensor,
+ UnknownMsg,
+))
+
+class FWLogMsg(ConstructClass):
+ subcon = Struct (
+ "msg_type" / Hex(Const(0x03, Int32ul)),
+ "seq_no" / Hex(Int32ul),
+ "timestamp" / Hex(Int64ul),
+ "msg" / PaddedString(0xc8, "ascii")
+ )
+
+class FaultMsg(ConstructClass):
+ subcon = Struct (
+ "msg_type" / Hex(Const(0, Int32ul)),
+ "unk_4" / HexDump(Bytes(0x34)),
+ )
+
+class FlagMsg(ConstructClass):
+ subcon = Struct (
+ "msg_type" / Hex(Const(1, Int32ul)),
+ "firing" / Array(2, Hex(Int64ul)),
+ "unk_14" / Hex(Int16ul),
+ "tail" / Bytes(0x38 - 0x18),
+ )
+
+class TimeoutMsg(ConstructClass):
+ subcon = Struct (
+ "msg_type" / Hex(Const(4, Int32ul)),
+ "counter" / Hex(Int32ul),
+ "unk_8" / Hex(Int32ul),
+ "stamp_index" / Hex(Int32sl),
+ "unkpad_16" / HexDump(Bytes(0x38 - 0x10)),
+ )
+
+EventMsg = FixedSized(0x38, Select(
+ FaultMsg,
+ FlagMsg,
+ TimeoutMsg,
+ HexDump(Bytes(0x38)),
+))
+
+TRACE_MSGS = {
+ (0x00, 0x00, 0): ("StartTA", "uuid", None, "unk", "cmdqueue"),
+ (0x00, 0x01, 0): ("FinishTA", "uuid", None, "unk", "cmdqueue"),
+ (0x00, 0x04, 0): ("Start3D", "uuid", "partial_render", "unk", "cmdqueue"),
+ (0x00, 0x05, 0): ("Finish3D_unk", "uuid", "unk", "flag", "buf_related"),
+ (0x00, 0x06, 0): ("Finish3D", "uuid", None, "unk", "cmdqueue"),
+ (0x00, 0x07, 0): ("StartCP", "uuid", None, "unk", "cmdqueue"),
+ (0x00, 0x08, 0): ("FinishCP", "uuid", None, "unk", "cmdqueue"),
+ (0x00, 0x0a, 0): ("StampUpdateTA", "value", "ev_id", "addr", "uuid"),
+ (0x00, 0x0c, 0): ("StampUpdate3D", "value", "ev_id", "addr", "uuid"),
+ (0x00, 0x0e, 0): ("StampUpdateCL", "value", "ev_id", "addr", "uuid"),
+ (0x00, 0x10, 1): ("TAPreproc1", "unk"),
+ (0x00, 0x10, 2): ("TAPreproc2", "unk1", "unk2"),
+ (0x00, 0x17, 0): ("Finish3D2", "uuid", None, "unk", "cmdqueue"),
+ (0x00, 0x28, 0): ("EvtNotify", "firing0", "firing1", "firing2", "firing3"),
+ (0x00, 0x2f, 0): ("Finish3D_unk2", "uuid", "unk"),
+ (0x00, 0x1e, 0): ("CleanupPB", "uuid", "unk2", "slot"),
+ (0x01, 0x0a, 0): ("Postproc", "cmdid", "event_ctl", "stamp_val", "uuid"),
+ (0x01, 0x0b, 0): ("EvtComplete", None, "event_ctl"),
+ (0x01, 0x0d, 0): ("EvtDequeued", "next", "event_ctl"),
+ (0x01, 0x16, 0): ("InitAttachment", "idx", "flags", "addr", "size"),
+ (0x01, 0x18, 0): ("ReInitAttachment", "idx", "flags", "addr", "size"),
+}
+
+class KTraceMsg(ConstructClass):
+ THREADS = [
+ "irq",
+ "bg",
+ "smpl",
+ "pwr",
+ "rec",
+ "kern",
+ ]
+ subcon = Struct (
+ "msg_type" / Hex(Const(5, Int32ul)),
+ "timestamp" / Hex(Int64ul),
+ "args" / Array(4, Int64ul),
+ "code" / Int8ul,
+ "channel" / Int8ul,
+ "pad" / Const(0, Int8ul),
+ "thread" / Int8ul,
+ "unk_flag" / Int64ul,
+ )
+ def __str__(self):
+ ts = self.timestamp / 24000000
+ code = (self.channel, self.code, self.unk_flag)
+ if code in TRACE_MSGS:
+ info = TRACE_MSGS[code]
+ args = info[0] + ": " + " ".join(f"{k}={v:#x}" for k, v in zip(info[1:], self.args) if k is not None)
+ else:
+ args = "UNK: " + ", ".join(hex(i) for i in self.args)
+ return f"TRACE: [{ts:10.06f}][{self.THREADS[self.thread]:4s}] {self.channel:2x}:{self.code:2x} ({self.unk_flag}) {args}"
+
+class FWCtlMsg(ConstructClass):
+ subcon = Struct (
+ "addr" / Int64ul,
+ "unk_8" / Int32ul,
+ "context_id" / Int32ul,
+ "unk_10" / Int16ul,
+ "unk_12" / Int16ul,
+ )
+
+channelNames = [
+ "TA_0", "3D_0", "CL_0",
+ "TA_1", "3D_1", "CL_1",
+ "TA_2", "3D_2", "CL_2",
+ "TA_3", "3D_3", "CL_3",
+ "DevCtrl",
+ "Event", "FWLog", "KTrace", "Stats",
+
+ ## Not really in normal order
+ "FWCtl"
+]
+
+# Exclude FWCtl
+CHANNEL_COUNT = len(channelNames) - 1
+
+channelRings = (
+ [[(RunCmdQueueMsg, 0x30, 0x100)]] * 12 + [
+ [(DeviceControlMsg, 0x30, 0x100)],
+ [(EventMsg, 0x38, 0x100)],
+ [
+ (FWLogMsg, 0xd8, 0x100), # unk 0
+ (FWLogMsg, 0xd8, 0x100), # init log
+ (FWLogMsg, 0xd8, 0x100), # unk 2
+ (FWLogMsg, 0xd8, 0x100), # warnings?
+ (FWLogMsg, 0xd8, 0x100), # unk 4
+ (FWLogMsg, 0xd8, 0x100), # unk 5
+ ],
+ [(KTraceMsg, 0x38, 0x200)],
+ [(HexDump(Bytes(0x60)), 0x60, 0x100)],
+ [(FWCtlMsg, 0x14, 0x100)],
+ ]
+)
+
+class ChannelStateFields(RegMap):
+ _SIZE = 0x30
+
+ READ_PTR = 0x00, Register32
+ WRITE_PTR = 0x20, Register32
+
+class FWControlStateFields(RegMap):
+ _SIZE = 0x20
+
+ READ_PTR = 0x00, Register32
+ WRITE_PTR = 0x10, Register32
+
+class Channel(Reloadable):
+ def __init__(self, u, uat, info, ring_defs, base=None, state_fields=ChannelStateFields):
+ self.uat = uat
+ self.u = u
+ self.p = u.proxy
+ self.iface = u.iface
+
+ self.ring_defs = ring_defs
+ self.info = info
+
+ self.accessor = uat.ioaccessor(0)
+ self.state_addr = info.state_addr
+ self.state = []
+ self.rb_base = []
+ self.rb_maps = []
+
+ if base is None:
+ p = info.ringbuffer_addr
+ else:
+ p = base
+ for i, (msg, size, count) in enumerate(ring_defs):
+ assert msg.sizeof() == size
+
+ self.state.append(state_fields(self.accessor, self.state_addr + 0x30 * i))
+ m = uat.iotranslate(0, p, size * count)
+ self.rb_base.append(p)
+ self.rb_maps.append(m)
+ p += size * count
+
+ def get_message(self, ring, index, meta_fn=None):
+ msgcls, size, count = self.ring_defs[ring]
+
+ assert index < count
+ addr = self.rb_base[ring] + index * size
+ stream = self.uat.iostream(0, addr)
+ stream.meta_fn = meta_fn
+ return msgcls.parse_stream(stream)
+
+ def clear_message(self, ring, index):
+ msgcls, size, count = self.ring_defs[ring]
+
+ self.put_message(ring, index, b"\xef\xbe\xad\xde" * (size // 4))
+
+ def put_message(self, ring, index, obj):
+ msgcls, size, count = self.ring_defs[ring]
+
+ assert index < count
+ if isinstance(obj, bytes):
+ data = obj
+ else:
+ data = obj.build()
+ self.uat.iowrite(0, self.rb_base[ring] + index * size, data)
+
+class ChannelInfo(ConstructClass):
+ subcon = Struct(
+ "state_addr" / Hex(Int64ul),
+ "ringbuffer_addr" / Hex(Int64ul),
+ )
+
+class ChannelInfoSet(ConstructClass):
+ CHAN_COUNT = CHANNEL_COUNT
+
+ subcon = Struct(*[ name / ChannelInfo for name in channelNames[:CHAN_COUNT]])
+
+__all__.extend(k for k, v in globals().items()
+ if (callable(v) or isinstance(v, type)) and v.__module__ == __name__)
diff --git a/tools/proxyclient/m1n1/fw/agx/cmdqueue.py b/tools/proxyclient/m1n1/fw/agx/cmdqueue.py
new file mode 100644
index 0000000..bd90a05
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/agx/cmdqueue.py
@@ -0,0 +1,516 @@
+# SPDX-License-Identifier: MIT
+from m1n1.constructutils import *
+from construct import *
+from .microsequence import *
+from ...utils import RegMap, Register32
+
+__all__ = []
+
+class WorkCommandBarrier(ConstructClass):
+ """
+ sent before WorkCommand3D on the Submit3d queue.
+ Might be for initilzing the tile buckets?
+
+ Example:
+ 00000004 0c378018 ffffffa0 00000c00 00000006 00000900 08002c9a 00000000
+ 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+ """
+ subcon = Struct(
+ "magic" / Const(0x4, Int32ul),
+ "stamp_addr" / Int64ul,
+ "stamp" / ROPointer(this.stamp_addr, StampCounter),
+ "wait_value" / Int32ul,
+ "event" / Int32ul, # Event number that signals a stamp check
+ "stamp_self" / Int32ul,
+ "uuid" / Int32ul,
+ "unk" / Default(Int32ul, 0),
+ )
+
+class WorkCommandInitBM(ConstructClass):
+ """
+ occationally sent before WorkCommandTA on the SubmitTA queue.
+
+ Example:
+ 00000004 0c378018 ffffffa0 00000c00 00000006 00000900 08002c9a 00000000
+ 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+ """
+ subcon = Struct(
+ "magic" / Const(0x6, Hex(Int32ul)),
+ "context_id" / Hex(Int32ul), # Might be context?
+ "buffer_mgr_slot" / Hex(Int32ul), # 0
+ "unk_c" / Hex(Int32ul), # 0
+ "unk_10" / Hex(Int32ul), # 0x30
+ "buffer_mgr_addr" / Int64ul,
+ "buffer_mgr" / ROPointer(this.buffer_mgr_addr, BufferManagerInfo),
+ "stamp_value" / Hex(Int32ul), # 0x100
+ )
+
+class LinkedListHead(ConstructClass):
+ subcon = Struct(
+ "prev" / Int64ul,
+ "next" / Int64ul,
+ )
+
+ def __init__(self):
+ super().__init__()
+ self.prev = 0
+ self.next = 0
+
+class EventControlUnkBuf(ConstructValueClass):
+ subcon = HexDump(Bytes(0x8))
+
+ def __init__(self):
+ super().__init__()
+ self.value = b"\xff" * 8
+
+class EventControl(ConstructClass):
+ subcon = Struct(
+ "event_count_addr" / Int64ul,
+ "event_count" / ROPointer(this.event_count_addr, Int32ul),
+ "generation" / Int32ul,
+ "cur_count" / Int32ul,
+ "unk_10" / Int32ul,
+ "unk_14" / Int32ul,
+ "unk_18" / Int64ul,
+ "unk_20" / Int32ul,
+ "vm_slot" / Int32ul,
+ "has_ta" / Int32ul,
+ "pstamp_ta" / Array(4, Int64ul),
+ "has_3d" / Int32ul,
+ "pstamp_3d" / Array(4, Int64ul),
+ "has_cp" / Int32ul,
+ "pstamp_cp" / Array(4, Int64ul),
+ "in_list" / Int32ul,
+ Ver("G >= G14", "unk_98_g14_0" / HexDump(Bytes(0x14))),
+ "list_head" / LinkedListHead,
+ Ver("G >= G14", "unk_a8_g14_0" / Padding(4)),
+ Ver("V >= V13_0B4", "unk_buf" / EventControlUnkBuf),
+ )
+
+ def __init__(self):
+ super().__init__()
+ self.unk_14 = 0
+ self.unk_18 = 0
+ self.unk_20 = 0
+ self.vm_slot = 0
+ self.has_ta = 0
+ self.pstamp_ta = [0]*4
+ self.has_3d = 0
+ self.pstamp_3d = [0]*4
+ self.has_cp = 0
+ self.pstamp_cp = [0]*4
+ self.in_list = 0
+ self.unk_98_g14_0 = bytes(0x14)
+ self.list_head = LinkedListHead()
+ self.unk_buf = EventControlUnkBuf()
+
+class WorkCommandCP(ConstructClass):
+ """
+ For compute
+
+ Example:
+ 00000000 00000003 00000000 00000004 0c3d80c0 ffffffa0 00000000 00000000 00000000
+ 00000020 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+ 00000040 *
+ 00000060 00000000 00000000 00088000 00000015 00078000 00000015 000a6300 00000015
+ 00000080 000a6308 00000015 000a6310 00000015 000a6318 00000015 00000000 00000011
+ 000000a0 00008c60 00000000 00000041 00000000 000e8000 00000015 00000040 00000000
+ 000000c0 00000001 00000000 0000001c 00000000 00000000 00000000 00000000 00000000
+ 000000e0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+ 00000100 *
+ 000001e0 00000000 00000000 0c311cc0 ffffffa0 00000240 00000000 00000000 00000000
+ 00000200 00000000 00000000 00000000 00000000 00000000 00000000 00088000 00000015
+ 00000220 00078024 00000015 00000000 00000000 00000000 00000000 00000000 00000000
+ 00000240 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+ 00000260 110022b3 00000000 ffffffff 00000500 00000015 00000000 00000000 00000000
+ 00000280 000c8014 ffffffa0 0c378014 ffffffa0 00003b00 00000005 00000000 00000000
+ 000002a0 120022b8 00000000 00000000 00000000 00029030 ffffffa0 00029038 ffffffa0
+ 000002c0 00000000 00000000 00000000 00000000 00000015 00000000 00000000 00000000
+ 000002e0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+ """
+
+ subcon = Struct(
+ "addr" / Tell,
+ "magic" / Const(0x3, Hex(Int32ul)),
+ "unk_4" / Hex(Int32ul),
+ "context_id" / Hex(Int32ul),
+ "event_control_addr" / Hex(Int64ul),
+ "event_control" / ROPointer(this.event_control_addr, EventControl),
+
+ # This struct embeeds some data that the Control List has pointers back to, but doesn't
+ # seem to be actually part of this struct
+ Padding(0x1e8 - 0x14),
+
+ # offset 000001e8
+ "microsequence_ptr" / Hex(Int64ul),
+ "microsequence_size" / Hex(Int32ul),
+ "microsequence" / ROPointer(this.microsequence_ptr, MicroSequence),
+ )
+
+class WorkCommand0_UnkBuf(ConstructValueClass):
+ subcon = HexDump(Bytes(0x18))
+
+ def __init__(self):
+ self.value = bytes(0x18)
+
+class WorkCommand1_UnkBuf(ConstructValueClass):
+ subcon = HexDump(Bytes(0x110))
+
+ def __init__(self):
+ self.value = bytes(0x110)
+
+class WorkCommand1_UnkBuf2(ConstructClass):
+ subcon = Struct(
+ "unk_0" / Int64ul,
+ "unk_8" / Int64ul,
+ "unk_10" / Int64ul,
+ )
+
+class Flag(ConstructValueClass):
+ subcon = Hex(Int32ul)
+
+ def __init__(self):
+ self.value = 0
+
+class WorkCommand3D(ConstructClass):
+ """
+ For 3D
+
+ Example: 0xfa00c095640
+ 00000000 00000001 00000004 00000000 0c2d5f00 ffffffa0 000002c0 0c3d80c0 ffffffa0
+ 00000020 0c3e0000 ffffffa0 0c3e0100 ffffffa0 0c3e09c0 ffffffa0 01cb0000 00000015
+ 00000040 00000088 00000000 00000001 0010000c 00000000 00000000 00000000 00000000
+ 00000060 3a8de3be 3abd2fa8 00000000 00000000 0000076c 00000000 0000a000 00000000
+ 00000080 ffff8002 00000000 00028044 00000000 00000088 00000000 005d0000 00000015
+ 000000a0 00758000 00000015 0000c000 00000000 00000640 000004b0 0257863f 00000000
+ 000000c0 00000000 00000000 00000154 00000000 011d0000 00000015 011d0000 00000015
+ 000000e0 0195c000 00000015 0195c000 00000015 00000000 00000000 00000000 00000000
+ 00000100 00000000 00000000 00000000 00000000 0193c000 00000015 00000000 00000000
+ 00000120 0193c000 00000015 00000000 00000000 01b64000 00000015 00000000 00000000
+ 00000140 01b64000 00000015 00000000 00000000 01cb0000 00000015 01cb4000 00000015
+ 00000160 c0000000 00000003 01cb4000 00000015 00010280 00000000 00a38000 00000015
+ 00000180 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+ 000001a0 00000000 00000000 00000000 00000000 00000000 00000011 00008c60 00000000
+ 000001c0 00000000 00000000 00000000 00000000 0000001c 00000000 00000000 00000000
+ 000001e0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+ 00000200 *
+ 000003c0 00000012 00028084 00000000 00000000 3a8de3be 3abd2fa8 00000000 00000000
+ 000003e0 0010000c 00000000 00025031 00000004 3f800000 00000700 00000000 00000001
+ """
+
+ subcon = Struct(
+ "addr" / Tell,
+ "magic" / Const(0x1, Hex(Int32ul)),
+ Ver("V >= V13_0B4", "counter" / Int64ul),
+ "context_id" / Hex(Int32ul),
+ "unk_8" / Hex(Int32ul),
+ "microsequence_ptr" / Hex(Int64ul), # Command list
+ "microsequence_size" / Hex(Int32ul),
+ "microsequence" / ROPointer(this.microsequence_ptr, MicroSequence),
+ "event_control_addr" / Hex(Int64ul),
+ "event_control" / ROPointer(this.event_control_addr, EventControl),
+ "buffer_mgr_addr" / Int64ul,
+ "buffer_mgr" / ROPointer(this.buffer_mgr_addr, BufferManagerInfo),
+ "buf_thing_addr" / Int64ul,
+ "buf_thing" / ROPointer(this.buf_thing_addr, BufferThing),
+ "unk_emptybuf_addr" / Hex(Int64ul),
+ "tvb_tilemap" / Hex(Int64ul),
+ "unk_40" / Hex(Int64ul),
+ "unk_48" / Hex(Int32ul),
+ "tile_blocks_y" / Hex(Int16ul), # * 4
+ "tile_blocks_x" / Hex(Int16ul), # * 4
+ "unk_50" / Hex(Int64ul),
+ "unk_58" / Hex(Int64ul),
+ "merge_upper_x" / Hex(Float32l),
+ "merge_upper_y" / Hex(Float32l),
+ "unk_68" / Hex(Int64ul),
+ "tile_count" / Hex(Int64ul),
+
+ # Embedded structures that are also pointed to by other stuff
+ "struct_2" / Start3DStruct2,
+ "struct_1" / Start3DStruct1,
+ "unk_758" / Flag,
+ "unk_75c" / Flag,
+ "unk_buf" / WorkCommand1_UnkBuf,
+ "busy_flag" / Flag,
+ "struct_6" / Start3DStruct6,
+ "struct_7" / Start3DStruct7,
+ "unk_buf2" / WorkCommand1_UnkBuf2,
+ "ts1" / TimeStamp,
+ "ts2" / TimeStamp,
+ "ts3" / TimeStamp,
+ "unk_914" / Int32ul,
+ "unk_918" / Int64ul,
+ "unk_920" / Int32ul,
+ "unk_924" / Int32ul,
+ Ver("V >= V13_0B4", "unk_928_0" / Int32ul),
+ Ver("V >= V13_0B4", "unk_928_4" / Int8ul),
+ Ver("V >= V13_0B4", "ts_flag" / TsFlag),
+ Ver("V >= V13_0B4", "unk_5e6" / Default(Int16ul, 0)),
+ Ver("V >= V13_0B4", "unk_5e8" / Default(HexDump(Bytes(0x20)), bytes(0x20))),
+ "pad_928" / Default(HexDump(Bytes(0x18)), bytes(0x18)),
+ )
+
+class WorkCommand0_UnkBuf(ConstructValueClass):
+ subcon = HexDump(Bytes(0x18))
+
+ def __init__(self):
+ super().__init__()
+ self.value = bytes(0x18)
+
+class WorkCommandTA(ConstructClass):
+ """
+ For TA
+
+ Example:
+ 00000000 00000000 00000004 00000000 0c3d80c0 ffffffa0 00000002 00000000 0c3e0000
+ 00000020 ffffffa0 0c3e0100 ffffffa0 0c3e09c0 ffffffa0 00000000 00000200 00000000
+ 00000040 1e3ce508 1e3ce508 01cb0000 00000015 00000000 00000000 00970000 00000015
+ 00000060 01cb4000 80000015 006b0003 003a0012 00000001 00000000 00000000 00000000
+ 00000080 0000a000 00000000 00000088 00000000 01cb4000 00000015 00000000 00000000
+ 000000a0 0000ff00 00000000 007297a0 00000015 00728120 00000015 00000001 00000000
+ 000000c0 00728000 00040015 009f8000 00000015 00000000 00000000 00000000 00000000
+ 000000e0 0000a441 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+ 00000100 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000011
+ 00000120 00000000 00000000 0000001c 00000000 00008c60 00000000 00000000 00000000
+ 00000140 00000000 00000000 00000000 00000000 0000001c 00000000 00000000 00000000
+ 00000160 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+ 00000180 *
+ 000003a0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 000000f0
+ 000003c0 00000088 00000202 04af063f 00025031 00404030 00303024 000000c0 00000180
+ 000003e0 00000100 00008000 00000000 00000000 00000000 00000000 00000000 00000000
+ """
+
+ subcon = Struct(
+ "addr" / Tell,
+ "magic" / Const(0x0, Hex(Int32ul)),
+ Ver("V >= V13_0B4", "counter" / Int64ul),
+ "context_id" / Hex(Int32ul),
+ "unk_8" / Hex(Int32ul),
+ "event_control_addr" / Hex(Int64ul),
+ "event_control" / ROPointer(this.event_control_addr, EventControl),
+ "buffer_mgr_slot" / Hex(Int64ul),
+ "buffer_mgr_addr" / Int64ul,
+ "buffer_mgr" / ROPointer(this.buffer_mgr_addr, BufferManagerInfo),
+ "buf_thing_addr" / Int64ul,
+ "buf_thing" / ROPointer(this.buf_thing_addr, BufferThing),
+ "unk_emptybuf_addr" / Hex(Int64ul),
+ "unk_34" / Hex(Int32ul),
+
+ # Embedded structures that are also pointed to by other stuff
+ "struct_2" / StartTACmdStruct2, # 0x11c bytes
+ "unk_154" / HexDump(Bytes(0x268)), # unknown
+ "tiling_params" / TilingParameters, # 0x2c bytes
+ "unk_3e8" / HexDump(Bytes(0x74)), # unknown
+
+ "unkptr_45c" / Int64ul,
+ "tvb_size" / Int64ul,
+ "microsequence_ptr" / Hex(Int64ul),
+ "microsequence_size" / Hex(Int32ul),
+ "microsequence" / ROPointer(this.microsequence_ptr, MicroSequence),
+ "ev_3d" / Int32ul,
+ "stamp_value" / Int32ul,
+
+ "struct_3" / StartTACmdStruct3, # 0x114 bytes
+
+ "unk_594" / WorkCommand0_UnkBuf,
+
+ "ts1" / TimeStamp,
+ "ts2" / TimeStamp,
+ "ts3" / TimeStamp,
+
+ "unk_5c4" / Int32ul,
+ "unk_5c8" / Int32ul,
+ "unk_5cc" / Int32ul,
+ "unk_5d0" / Int32ul,
+ "unk_5d4" / Int8ul,
+ "pad_5d5" / Default(HexDump(Bytes(0x3)), bytes(0x3)),
+ Ver("V >= V13_0B4", "unk_5e0" / Int32ul),
+ Ver("V >= V13_0B4", "unk_5e4" / Int8ul),
+ Ver("V >= V13_0B4", "ts_flag" / TsFlag),
+ Ver("V >= V13_0B4", "unk_5e6" / Default(Int16ul, 0)),
+ Ver("V >= V13_0B4", "unk_5e8" / Default(HexDump(Bytes(0x18)), bytes(0x18))),
+ "pad_5d8" / Default(HexDump(Bytes(0x8)), bytes(0x8)),
+ Ver("V >= V13_0B4", "pad_5e0" / Default(HexDump(Bytes(0x18)), bytes(0x18))),
+ )
+
+class UnknownWorkCommand(ConstructClass):
+ subcon = Struct(
+ "magic" / Hex(Int32ul),
+ "unk_4" / Hex(Int32ul),
+ "unk_8" / Hex(Int32ul),
+ "unk_c" / Hex(Int32ul),
+ "unk_10" / Hex(Int32ul),
+ "unk_14" / Hex(Int32ul),
+ "unk_18" / Hex(Int32ul),
+ "unk_1c" / Hex(Int32ul),
+ )
+
+class CmdBufWork(ConstructClass):
+ subcon = Struct(
+ "cmdid" / Peek(Int32ul),
+ "cmd" / Switch(this.cmdid, {
+ 0: WorkCommandTA,
+ 1: WorkCommand3D,
+ 3: WorkCommandCP,
+ 4: WorkCommandBarrier,
+ 6: WorkCommandInitBM,
+ })
+ )
+
+class JobList(ConstructClass):
+ subcon = Struct(
+ "first_job" / Default(Int64ul, 0),
+ "last_head" / Int64ul,
+ "unkptr_10" / Default(Int64ul, 0),
+ )
+
+class GPUContextData(ConstructClass):
+ subcon = Struct(
+ "unk_0" / Int8ul,
+ "unk_1" / Int8ul,
+ "unk_2" / Default(Bytes(3), bytes(3)),
+ "unk_5" / Int8ul,
+ "unk_6" / Default(Bytes(0x18), bytes(0x18)),
+ "unk_1e" / Int8ul,
+ "unk_1f" / Int8ul,
+ "unk_20" / Default(Bytes(3), bytes(3)),
+ "unk_23" / Int8ul,
+ "unk_24" / Default(Bytes(0x1c), bytes(0x1c)),
+ )
+
+ def __init__(self):
+ self.unk_0 = 0xff
+ self.unk_1 = 0xff
+ self.unk_5 = 1
+ self.unk_1e = 0xff
+ self.unk_1f = 0
+ self.unk_23 = 2
+
+class CommandQueuePointerMap(RegMap):
+ GPU_DONEPTR = 0x00, Register32
+ GPU_RPTR = 0x30, Register32
+ CPU_WPTR = 0x40, Register32
+
+class CommandQueuePointers(ConstructClass):
+ subcon = Struct(
+ "gpu_doneptr" / Int32ul,
+ ZPadding(12),
+ "unk_10" / Int32ul,
+ ZPadding(12),
+ "unk_20" / Int32ul,
+ ZPadding(12),
+ "gpu_rptr" / Int32ul,
+ ZPadding(12),
+ "cpu_wptr" / Int32ul,
+ ZPadding(12),
+ "rb_size" / Int32ul,
+ ZPadding(12),
+ )
+
+ def __init__(self):
+ super().__init__()
+ self.gpu_doneptr = 0
+ self.unk_10 = 0
+ self.unk_20 = 0
+ self.gpu_rptr = 0
+ self.cpu_wptr = 0
+ self.rb_size = 0x500
+
+class CommandQueueInfo(ConstructClass):
+ """ Structure type shared by Submit3D, SubmitTA and SubmitCompute
+ Applications have multiple of these, one of each submit type
+ TODO: Can applications have more than one of each type? One per encoder?
+ Mostly managed by GPU, only intialize by CPU
+
+ """
+ subcon = Struct(
+ "pointers_addr" / Hex(Int64ul),
+ "pointers" / ROPointer(this.pointers_addr, CommandQueuePointers),
+ "rb_addr" / Hex(Int64ul), # 0x4ff pointers
+ "job_list_addr" / Hex(Int64ul), # ffffffa000000000, size 0x18 (shared by 3D and TA)
+ "job_list" / ROPointer(this.job_list_addr, JobList),
+ "gpu_buf_addr" / Hex(Int64ul), # GPU space for this queue, 0x2c18 bytes?
+ #"gpu_buf" / ROPointer(this.gpu_buf_addr, HexDump(Bytes(0x2c18))),
+ "gpu_rptr1" / Hex(Int32ul),
+ "gpu_rptr2" / Hex(Int32ul),
+ "gpu_rptr3" / Hex(Int32ul),
+ "event_id" / Int32sl,
+ "unk_30" / Hex(Int32ul), # read by CPU
+ "unk_34" / Hex(Int32ul),
+ "unk_38" / Hex(Int64ul),
+ "unk_40" / Hex(Int32ul), # 1
+ "unk_44" / Hex(Int32ul), # 0
+ "unk_48" / Hex(Int32ul), # 1, 2
+ "unk_4c" / Int32sl, # -1
+ "uuid" / Hex(Int32ul), # Counts up for each new process or command queue
+ "unk_54" / Int32sl,
+ "unk_58" / Hex(Int64ul), # 0
+ "busy" / Hex(Int32ul), # 1 = gpu busy
+ "pad1" / ZPadding(0x20),
+ "blocked_on_barrier" / Hex(Int32ul),
+ "unk_88" / Int32ul,
+ "unk_8c" / Int32ul,
+ "unk_90" / Int32ul,
+ "unk_94" / Int32ul,
+ "pending" / Int32ul,
+ "unk_9c" / Int32ul,
+ "gpu_context_addr" / Hex(Int64ul), # GPU managed context, shared between 3D and TA. Passed to DC_DestroyContext
+ "gpu_context" / ROPointer(this.gpu_context_addr, GPUContextData),
+ "unk_a8" / Int64ul
+ # End of struct
+ )
+
+ def __init__(self):
+ super().__init__()
+ self.gpu_rptr1 = 0
+ self.gpu_rptr2 = 0
+ self.gpu_rptr3 = 0
+ self.event_id = -1
+ self.unk_4c = -1
+ self.uuid = 0xdeadbeef # some kind of ID
+ self.unk_54 = -1
+ self.unk_58 = 0x0
+ self.busy = 0x0
+ self.blocked_on_barrier = 0x0
+ self.unk_88 = 0
+ self.unk_8c = 0
+ self.unk_90 = 0
+ self.unk_94 = 0
+ self.pending = 0
+ self.unk_9c = 0
+ self.set_prio(0)
+ self.unk_a8 = 0
+
+ def set_prio(self, p):
+ if p == 0:
+ self.unk_30 = 0
+ self.unk_34 = 0 # 0-3?
+ self.unk_38 = 0xffff_ffff_ffff_0000
+ self.unk_40 = 1
+ self.unk_44 = 0
+ self.unk_48 = 1
+ elif p == 1:
+ self.unk_30 = 1
+ self.unk_34 = 1
+ self.unk_38 = 0xffff_ffff_0000_0000
+ self.unk_40 = 0
+ self.unk_44 = 0
+ self.unk_48 = 0
+ elif p == 2:
+ self.unk_30 = 2
+ self.unk_34 = 2
+ self.unk_38 = 0xffff_0000_0000_0000
+ self.unk_40 = 0
+ self.unk_44 = 0
+ self.unk_48 = 2
+ else:
+ self.unk_30 = 3
+ self.unk_34 = 3
+ self.unk_38 = 0x0000_0000_0000_0000
+ self.unk_40 = 0
+ self.unk_44 = 0
+ self.unk_48 = 3
+
+__all__.extend(k for k, v in globals().items()
+ if (callable(v) or isinstance(v, type)) and v.__module__ == __name__)
diff --git a/tools/proxyclient/m1n1/fw/agx/handoff.py b/tools/proxyclient/m1n1/fw/agx/handoff.py
new file mode 100644
index 0000000..4f9acf0
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/agx/handoff.py
@@ -0,0 +1,120 @@
+# SPDX-License-Identifier: MIT
+from ...utils import *
+from contextlib import contextmanager
+
+PPL_MAGIC = 0x4b1d000000000002
+
+class GFXHandoffStruct(RegMap):
+ MAGIC_AP = 0x0, Register64
+ MAGIC_FW = 0x8, Register64
+
+ LOCK_AP = 0x10, Register8
+ LOCK_FW = 0x11, Register8
+ TURN = 0x14, Register32
+
+ CUR_CTX = 0x18, Register32
+
+ FLUSH_STATE = irange(0x20, 0x41, 0x18), Register64
+ FLUSH_ADDR = irange(0x28, 0x41, 0x18), Register64
+ FLUSH_SIZE = irange(0x30, 0x41, 0x18), Register64
+
+ UNK2 = 0x638, Register8
+ UNK3 = 0x640, Register64
+
+class GFXHandoff:
+ def __init__(self, u):
+ self.u = u
+ self.sgx_dev = self.u.adt["/arm-io/sgx"]
+ self.base = self.sgx_dev.gfx_handoff_base
+ self.reg = GFXHandoffStruct(u, self.base)
+ self.is_locked = False
+ self.initialized = False
+
+ @contextmanager
+ def lock(self):
+ """Dekker's algorithm lock"""
+ assert not self.is_locked
+
+ # Note: This *absolutely* needs barriers everywhere.
+ # Those are implicit in proxyclient for every operation.
+
+ self.reg.LOCK_AP.val = 1
+ while self.reg.LOCK_FW.val != 0:
+ if self.reg.TURN != 0:
+ self.reg.LOCK_AP = 0
+ while self.reg.TURN != 0:
+ pass
+ self.reg.LOCK_AP = 1
+
+ self.is_locked = True
+ try:
+ yield
+ finally:
+ self.reg.TURN.val = 1
+ self.reg.LOCK_AP.val = 0
+ self.is_locked = False
+
+ def initialize(self):
+ if self.initialized:
+ return
+
+ print("[Handoff] Initializing...")
+
+ self.reg.MAGIC_AP.val = PPL_MAGIC
+ self.reg.UNK = 0xffffffff
+ self.reg.UNK3 = 0
+
+ with self.lock():
+ print("[Handoff] Waiting for FW PPL init...")
+ while self.reg.MAGIC_FW.val != PPL_MAGIC:
+ pass
+
+ for i in range(0x41):
+ self.reg.FLUSH_STATE[i].val = 0
+ self.reg.FLUSH_ADDR[i].val = 0
+ self.reg.FLUSH_SIZE[i].val = 0
+
+ self.initialized = True
+ print("[Handoff] Initialized!")
+
+ # The order here is:
+ # - Remap memory as shared
+ # - TLBI
+ # - prepare_cacheflush()
+ # - issue FWCtl request
+ # - wait for completion (ring or wait_cacheflush?)
+ # - Unmap memory
+ # - TLBI
+ # - complete_cacheflush()
+ def prepare_cacheflush(self, base, size, context=0x40):
+ assert self.reg.FLUSH_STATE[context].val == 0
+
+ self.reg.FLUSH_ADDR[context].val = base
+ self.reg.FLUSH_SIZE[context].val = size
+ self.reg.FLUSH_STATE[context].val = 1
+
+ def wait_cacheflush(self, context=0x40):
+ while self.reg.FLUSH_STATE[context].val == 1:
+ pass
+
+ def complete_cacheflush(self, context=0x40):
+ assert self.reg.FLUSH_STATE[context].val == 2
+ self.reg.FLUSH_STATE[context].val = 0
+
+ # probably not necessary?
+ # order is:
+ # - Remap memory as shared
+ # - (no TLBI?)
+ # - prepare_unmap()
+ # - unmap
+ # - TLBI
+ # - complete_unmap()
+ def prepare_unmap(self, base, size, context):
+ assert self.reg.FLUSH_STATE[context].val == 0
+ self.reg.FLUSH_ADDR[context].val = 0xdead000000000000 | (base & 0xffffffffffff)
+ self.reg.FLUSH_SIZE[context].val = size
+ self.reg.FLUSH_STATE[context].val = 2
+
+ def complete_unmap(self, context):
+ assert self.reg.FLUSH_STATE[context].val == 2
+ self.reg.FLUSH_STATE[context].val = 0
diff --git a/tools/proxyclient/m1n1/fw/agx/initdata.py b/tools/proxyclient/m1n1/fw/agx/initdata.py
new file mode 100644
index 0000000..ea8d4df
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/agx/initdata.py
@@ -0,0 +1,1931 @@
+from m1n1.utils import *
+from m1n1.constructutils import *
+from construct import *
+from construct.lib import hexundump
+
+from .channels import ChannelInfoSet, ChannelInfo
+
+__all__ = []
+
+class InitData_FWStatus(ConstructClass):
+ subcon = Struct(
+ "fwctl_channel" / ChannelInfo,
+ "halt_count" / Int32ul,
+ ZPadding(0xc),
+ "halted" / Int32ul,
+ ZPadding(0xc),
+ "resume" / Int32ul,
+ ZPadding(0xc),
+ "unk_40" / Int32ul,
+ ZPadding(0xc),
+ "unk_ctr" / Int32ul,
+ ZPadding(0xc),
+ "unk_60" / Int32ul,
+ ZPadding(0xc),
+ "unk_70" / Int32ul,
+ ZPadding(0xc),
+ )
+
+ def __init__(self):
+ super().__init__()
+ self.halt_count = 0
+ self.halted = 0
+ self.resume = 0
+ self.unk_40 = 0
+ self.unk_ctr = 0
+ self.unk_60 = 0
+ self.unk_70 = 0
+
+class AGXHWDataShared1(ConstructClass):
+ subcon = Struct(
+ "table" / Array(17, Int32sl),
+ "unk_44" / HexDump(Bytes(0x60)),
+ "unk_a4" / Int32ul,
+ "unk_a8" / Int32ul,
+ )
+
+ def __init__(self, chip_info):
+ super().__init__()
+ self.table = chip_info.shared1_tab
+ self.unk_44 = bytes(0x60)
+ self.unk_a4 = chip_info.shared1_a4
+ self.unk_a8 = 0
+
+class AGXHWDataShared2Curve(ConstructClass):
+ subcon = Struct(
+ "unk_0" / Int32ul,
+ "unk_4" / Int32ul,
+ "t1" / Array(16, Int16sl),
+ "t2" / Array(16, Int16sl),
+ "t3" / Array(8, Array(16, Int32sl)),
+ )
+
+ def __init__(self, unk_0=0, unk_4=0, t1=None, t2=None, t3=None):
+ self.unk_0 = unk_0
+ self.unk_4 = unk_4
+ if not t1:
+ self.t1 = [0] * 16
+ else:
+ self.t1 = t1 + [t1[0]] * (16 - len(t1))
+ if not t2:
+ self.t2 = [0] * 16
+ else:
+ self.t2 = t2 + [t2[0]] * (16 - len(t2))
+ if t3 is None:
+ self.t3 = [[0] * 16] * 8
+ else:
+ self.t3 = ([(i + [0x3ffffff] * (16 - len(i))) for i in t3] +
+ [[0x3ffffff] * 16] * (8 - len(t3)))
+
+
+class AGXHWDataShared2T8112(ConstructClass):
+ subcon = Struct(
+ "unk_0" / Array(5, Int32ul),
+ "unk_14" / Int32ul,
+ "unk_18" / Array(8, Int32ul),
+ "curve1" / AGXHWDataShared2Curve,
+ "curve2" / AGXHWDataShared2Curve,
+ )
+
+ def __init__(self, chip_info):
+ self.unk_0 = [0] * 5
+ self.unk_18 = [0] * 8
+
+ if chip_info.chip_id == 0x8112:
+ self.unk_14 = 0x6000000
+ self.curve1 = AGXHWDataShared2Curve(
+ 0, 0x20000000,
+ [-1], [0x0f07], [[]]
+ )
+ self.curve2 = AGXHWDataShared2Curve(
+ 7, 0x80000000,
+ [
+ -1, 25740, 17429, 12550, 9597, 7910, 6657, 5881, 5421
+ ], [
+ 0x0F07, 0x04C0, 0x06C0, 0x08C0,
+ 0x0AC0, 0x0C40, 0x0DC0, 0x0EC0,
+ 0x0F80
+ ], [
+ [0x03FFFFFF, 107, 101, 94, 87, 82, 77, 73, 71],
+ [0x03FFFFFF, 38240, 36251, 33562,
+ 31368, 29379, 27693, 26211, 25370],
+ [0x03FFFFFF, 123933, 117485, 108771,
+ 101661, 95217, 89751, 84948, 82222],
+ ]
+ )
+ else:
+ self.unk_14 = 0
+ self.curve1 = AGXHWDataShared2Curve()
+ self.curve2 = AGXHWDataShared2Curve()
+
+class AGXHWDataShared3(ConstructClass):
+ subcon = Struct(
+ "unk_0" / Int32ul,
+ "unk_4" / Int32ul,
+ "unk_8" / Int32ul,
+ "table" / Array(16, Int32ul),
+ "unk_4c" / Int32ul,
+ )
+
+ def __init__(self, chip_info):
+ if chip_info.chip_id == 0x8112:
+ self.unk_0 = 1
+ self.unk_4 = 500
+ self.unk_8 = 5
+ self.table = [
+ 10700, 10700, 10700, 10700,
+ 10700, 6000, 1000, 1000,
+ 1000, 10700, 10700, 10700,
+ 10700, 10700, 10700, 10700,
+ ]
+ self.unk_4c = 1
+ else:
+ self.unk_0 = 0
+ self.unk_4 = 0
+ self.unk_8 = 0
+ self.table = [0] * 16
+ self.unk_4c = 0
+
+class AGXHWDataShared2(ConstructClass):
+ subcon = Struct(
+ "table" / Array(10, Int32sl),
+ "unk_28" / HexDump(Bytes(0x10)),
+ "unk_38" / AGXHWDataShared2T8112,
+ "unk_500" / Int32ul,
+ "unk_504" / Int32ul,
+ "unk_508" / Int32ul,
+ "unk_50c" / Int32ul,
+ "unk_510" / Int32ul,
+ )
+
+ def __init__(self, chip_info):
+ super().__init__()
+ self.table = chip_info.shared2_tab
+ self.unk_20 = bytes(8)
+ self.unk_28 = b"\xff" * 16
+ self.unk_38 = AGXHWDataShared2T8112(chip_info)
+ self.unk_500 = 0
+ self.unk_504 = 0
+ self.unk_508 = chip_info.shared2_unk_508
+ self.unk_50c = 0
+ self.unk_510 = 0
+
+class AGXHWDataA130Extra(ConstructClass):
+ subcon = Struct(
+ "unk_0" / HexDump(Bytes(0x38)),
+ "unk_38" / Dec(Int32ul),
+ "unk_3c" / Dec(Int32ul),
+ "unk_40" / Dec(Int32ul),
+ "unk_44" / Int32ul,
+ "unk_48" / Int32ul,
+ "unk_4c" / Dec(Int32ul),
+ "unk_50" / Int32ul,
+ "unk_54" / Dec(Int32ul),
+ "unk_58" / Int32ul,
+ "unk_5c" / Int32ul,
+ "unk_60" / Float32l,
+ "unk_64" / Float32l,
+ "unk_68" / Float32l,
+ "unk_6c" / Float32l,
+ "unk_70" / Float32l,
+ "unk_74" / Float32l,
+ "unk_78" / Float32l,
+ "unk_7c" / Float32l,
+ "unk_80" / Float32l,
+ "unk_84" / Float32l,
+ "unk_88" / Int32ul,
+ "unk_8c" / Dec(Int32ul),
+ "unk_90" / Dec(Int32ul),
+ "unk_94" / Int32ul,
+ "unk_98" / Int32ul,
+ "unk_9c" / Float32l,
+ "unk_a0" / Dec(Int32ul),
+ "unk_a4" / Int32ul,
+ "unk_a8" / Dec(Int32ul),
+ "unk_ac" / Dec(Int32ul),
+ "unk_b0" / Dec(Int32ul),
+ "unk_b4" / Int32ul,
+ "unk_b8" / Dec(Int32ul),
+ "unk_bc" / Int32ul,
+ "unk_c0" / Int32ul,
+ "unk_c4" / Float32l,
+ "unk_c8" / HexDump(Bytes(0x4c)),
+ "unk_114" / Float32l,
+ "unk_118" / Int32ul,
+ "unk_11c" / Int32ul,
+ "unk_120" / Int32ul,
+ "unk_124" / Dec(Int32ul),
+ "unk_128" / Dec(Int32ul),
+ "unk_12c" / HexDump(Bytes(0x8c)),
+ )
+
+ def __init__(self):
+ super().__init__()
+ self.unk_0 = bytes(0x38)
+ self.unk_38 = 4
+ self.unk_3c = 8000
+ self.unk_40 = 2500
+ self.unk_44 = 0x0
+ self.unk_48 = 0xffffffff
+ self.unk_4c = 50
+ self.unk_50 = 0x0
+ self.unk_54 = 50
+ self.unk_58 = 0x1
+ self.unk_5c = 0x0
+ self.unk_60 = 0.88888888
+ self.unk_64 = 0.66666666
+ self.unk_68 = 0.111111111
+ self.unk_6c = 0.33333333
+ self.unk_70 = -0.4
+ self.unk_74 = -0.8
+ self.unk_78 = 0.0
+ self.unk_7c = 65536.0
+ self.unk_80 = -5.0
+ self.unk_84 = -10.0
+ self.unk_88 = 0x0
+ self.unk_8c = 40
+ self.unk_90 = 600
+ self.unk_94 = 0x0
+ self.unk_98 = 0x0
+ self.unk_9c = 8000.0
+ self.unk_a0 = 1400
+ self.unk_a4 = 0x0
+ self.unk_a8 = 72
+ self.unk_ac = 24
+ self.unk_b0 = 1728000
+ self.unk_b4 = 0x0
+ self.unk_b8 = 576000
+ self.unk_bc = 0x0
+ self.unk_c0 = 0x0
+ self.unk_c4 = 65536.0
+ self.unk_c8 = bytes(0x4c)
+ self.unk_114 = 65536.0
+ self.unk_118 = 0x0
+ self.unk_11c = 0x0
+ self.unk_120 = 0x0
+ self.unk_124 = 40
+ self.unk_128 = 600
+ self.unk_12c = bytes(0x8c)
+
+class AGXHWDataT81xx(ConstructClass):
+ subcon = Struct(
+ "unk_d8c" / Int32ul,
+ "unk_d90" / Int32ul,
+ "unk_d94" / Int32ul,
+ "unk_d98" / Int32ul,
+ "unk_d9c" / Float32l,
+ "unk_da0" / Int32ul,
+ "unk_da4" / Float32l,
+ "unk_da8" / Int32ul,
+ "unk_dac" / Float32l,
+ "unk_db0" / Int32ul,
+ "unk_db4" / Int32ul,
+ "unk_db8" / Float32l,
+ "unk_dbc" / Float32l,
+ "unk_dc0" / Int32ul,
+ "unk_dc4" / Int32ul,
+ "unk_dc8" / Int32ul,
+ "unk_dcc" / Int32ul,
+ )
+ def __init__(self, sgx, chip_info):
+ if chip_info.chip_id in (0x8103, 0x8112):
+ self.unk_d8c = 0x80000000
+ self.unk_d90 = 4
+ self.unk_d94 = 0
+ self.unk_d98 = 0
+ self.unk_d9c = 0.6
+ self.unk_da0 = 0
+ self.unk_da4 = 0.4
+ self.unk_da8 = 0
+ self.unk_dac = 0.38552
+ self.unk_db0 = 0
+ self.unk_db4 = 0
+ self.unk_db8 = 65536.0
+ self.unk_dbc = 13.56
+ self.unk_dc0 = 0
+ self.unk_dc4 = 0
+ self.unk_dc8 = 0
+ self.unk_dcc = 100 * sgx.gpu_num_perf_states
+ else:
+ self.unk_d8c = 0
+ self.unk_d90 = 0
+ self.unk_d94 = 0
+ self.unk_d98 = 0
+ self.unk_d9c = 0
+ self.unk_da0 = 0
+ self.unk_da4 = 0
+ self.unk_da8 = 0
+ self.unk_dac = 0
+ self.unk_db0 = 0
+ self.unk_db4 = 0
+ self.unk_db8 = 0
+ self.unk_dbc = 0
+ self.unk_dc0 = 0
+ self.unk_dc4 = 0
+ self.unk_dc8 = 0
+ self.unk_dcc = 0
+
+class PowerZone(ConstructClass):
+ subcon = Struct(
+ "val" / Float32l,
+ "target" / Dec(Int32ul),
+ "target_off" / Dec(Int32ul),
+ "filter_tc_x4" / Dec(Int32ul),
+ "filter_tc_xperiod" / Dec(Int32ul),
+ Ver("V >= V13_0B4", "unk_10" / Dec(Int32ul)),
+ Ver("V >= V13_0B4", "unk_14" / Dec(Int32ul)),
+ "filter_tc_neginv" / Float32l,
+ "filter_tc_inv" / Float32l,
+ "pad" / Int32ul,
+ )
+ def __init__(self, tc=None, target=None, off=None, period_ms=None):
+ self.val = 0.0
+ self.pad = 0
+ if tc is None:
+ self.target = 0
+ self.target_off = 0
+ self.filter_tc_x4 = 0
+ self.filter_tc_xperiod = 0
+ self.unk_10 = 0
+ self.unk_14 = 0
+ self.filter_tc_neginv = 0
+ self.filter_tc_inv = 0
+ else:
+ self.target = target
+ self.target_off = self.target - off
+ self.filter_tc_x4 = tc * 4
+ self.filter_tc_xperiod = tc * period_ms
+ self.unk_10 = 1320000000
+ self.unk_14 = 0
+ self.filter_tc_neginv = 1 / tc
+ self.filter_tc_inv = 1 - 1 / tc
+
+class AGXHWDataA(ConstructClass):
+ subcon = Struct(
+ "unk_0" / Int32ul,
+ "clocks_per_period" / Int32ul,
+ Ver("V >= V13_0B4", "clocks_per_period_2" / Int32ul),
+ "unk_8" / Int32ul,
+ "pwr_status" / Int32ul,
+ "unk_10" / Float32l,
+ "unk_14" / Int32ul,
+ "unk_18" / Int32ul,
+ "unk_1c" / Int32ul,
+ "unk_20" / Int32ul,
+ "unk_24" / Int32ul,
+ "actual_pstate" / Int32ul,
+ "tgt_pstate" / Int32ul,
+ "unk_30" / Int32ul,
+ "cur_pstate" / Int32ul,
+ "unk_38" / Int32ul,
+ Ver("V >= V13_0B4", "unk_3c_0" / Int32ul),
+ "base_pstate_scaled" / Int32ul,
+ "unk_40" / Int32ul,
+ "max_pstate_scaled" / Int32ul,
+ "unk_48" / Int32ul,
+ "min_pstate_scaled" / Int32ul,
+ "freq_mhz" / Float32l,
+ "unk_54" / HexDump(Bytes(0x20)),
+ Ver("V >= V13_0B4", "unk_74_0" / Int32ul),
+ "unk_74" / Array(16, Float32l),
+ "unk_b4" / HexDump(Bytes(0x100)),
+ "unk_1b4" / Int32ul,
+ "temp_c" / Int32ul,
+ "avg_power_mw" / Dec(Int32ul),
+ "update_ts" / Int64ul,
+ "unk_1c8" / Int32ul,
+ "unk_1cc" / HexDump(Bytes(0x644 - 0x1cc)),
+ "pad_644" / HexDump(Bytes(8)),
+
+ "unk_64c" / Int32ul,
+ "unk_650" / Int32ul,
+ "pad_654" / Int32ul,
+ "pwr_filter_a_neg" / Float32l,
+ "pad_65c" / Int32ul,
+ "pwr_filter_a" / Float32l,
+ "pad_664" / Int32ul,
+ "pwr_integral_gain" / Float32l,
+ "pad_66c" / Int32ul,
+ "pwr_integral_min_clamp" / Float32l,
+ "max_power_1" / Float32l,
+ "pwr_proportional_gain" / Float32l,
+ "pad_67c" / Int32ul,
+ "pwr_pstate_related_k" / Float32l,
+ "pwr_pstate_max_dc_offset" / Int32sl,
+ "unk_688" / Int32ul,
+ "max_pstate_scaled_2" / Int32ul,
+ "pad_690" / Int32ul,
+ "unk_694" / Int32ul,
+ "max_power_2" / Int32ul,
+
+ "pad_69c" / HexDump(Bytes(0x18)),
+
+ "unk_6b4" / Int32ul,
+ Ver("V >= V13_0B4", "unk_6b8_0" / HexDump(Bytes(0x10))),
+ "max_pstate_scaled_3" / Int32ul,
+ "unk_6bc" / Int32ul,
+
+ "pad_6c0" / HexDump(Bytes(0x14)),
+
+ "ppm_filter_tc_periods_x4" / Int32ul,
+ "unk_6d8" / Int32ul,
+
+ "pad_6dc" / Int32ul,
+
+ "ppm_filter_a_neg" / Float32l,
+ "pad_6e4" / Int32ul,
+ "ppm_filter_a" / Float32l,
+ "pad_6ec" / Int32ul,
+ "ppm_ki_dt" / Float32l,
+ "pad_6f4" / Int32ul,
+ "pwr_integral_min_clamp_2" / Int32ul,
+ "unk_6fc" / Float32l,
+ "ppm_kp" / Float32l,
+ "pad_704" / Int32ul,
+
+ "unk_708" / Int32ul,
+ "pwr_min_duty_cycle" / Int32ul,
+ "max_pstate_scaled_4" / Int32ul,
+ "unk_714" / Int32ul,
+
+ "pad_718" / Int32ul,
+
+ "unk_71c" / Float32l,
+ "max_power_3" / Int32ul,
+
+ "cur_power_mw_2" / Int32ul,
+
+ "ppm_filter_tc_ms" / Int32ul,
+ "unk_72c" / Int32ul,
+ Ver("V >= V13_0B4", "unk_730_0" / Int32ul),
+ Ver("V >= V13_0B4", "unk_730_4" / Int32ul),
+ Ver("V >= V13_0B4", "unk_730_8" / Int32ul),
+ Ver("V >= V13_0B4", "unk_730_c" / Int32ul),
+ "unk_730" / Float32l,
+ "unk_734" / Int32ul,
+
+ "unk_738" / Int32ul,
+ "unk_73c" / Int32ul,
+ "unk_740" / Int32ul,
+ "unk_744" / Int32ul,
+ "unk_748" / Array(4, Float32l),
+ "unk_758" / Int32ul,
+ "perf_tgt_utilization" / Int32ul,
+ "pad_760" / Int32ul,
+ "perf_boost_min_util" / Int32ul,
+ "perf_boost_ce_step" / Int32ul,
+ "perf_reset_iters" / Int32ul,
+ "pad_770" / Int32ul,
+ "unk_774" / Int32ul,
+ "unk_778" / Int32ul,
+ "perf_filter_drop_threshold" / Int32ul,
+
+ "perf_filter_a_neg" / Float32l,
+ "perf_filter_a2_neg" / Float32l,
+ "perf_filter_a" / Float32l,
+ "perf_filter_a2" / Float32l,
+ "perf_ki" / Float32l,
+ "perf_ki2" / Float32l,
+ "perf_integral_min_clamp" / Float32l,
+ "unk_79c" / Float32l,
+ "perf_kp" / Float32l,
+ "perf_kp2" / Float32l,
+ "boost_state_unk_k" / Float32l,
+
+ "base_pstate_scaled_2" / Dec(Int32ul),
+ "max_pstate_scaled_5" / Dec(Int32ul),
+ "base_pstate_scaled_3" / Dec(Int32ul),
+
+ "pad_7b8" / Int32ul,
+
+ "perf_cur_utilization" / Float32l,
+ "perf_tgt_utilization_2" / Int32ul,
+
+ "pad_7c4" / HexDump(Bytes(0x18)),
+
+ "unk_7dc" / Int32ul,
+ Ver("V >= V13_0B4", "unk_7e0_0" / HexDump(Bytes(0x10))),
+ "base_pstate_scaled_4" / Dec(Int32ul),
+ "pad_7e4" / Int32ul,
+
+ "unk_7e8" / HexDump(Bytes(0x14)),
+
+ "unk_7fc" / Float32l,
+ "pwr_min_duty_cycle_2" / Float32l,
+ "max_pstate_scaled_6" / Float32l,
+ "max_freq_mhz" / Int32ul,
+ "pad_80c" / Int32ul,
+ "unk_810" / Int32ul,
+ "pad_814" / Int32ul,
+ "pwr_min_duty_cycle_3" / Int32ul,
+ "unk_81c" / Int32ul,
+ "pad_820" / Int32ul,
+ "min_pstate_scaled_4" / Float32l,
+ "max_pstate_scaled_7" / Dec(Int32ul),
+ "unk_82c" / Int32ul,
+ "unk_alpha_neg" / Float32l,
+ "unk_alpha" / Float32l,
+ "unk_838" / Int32ul,
+ "unk_83c" / Int32ul,
+ "pad_840" / HexDump(Bytes(0x86c - 0x838 - 8)),
+
+ "unk_86c" / Int32ul,
+ "fast_die0_sensor_mask64" / Int64ul,
+ "fast_die0_release_temp_cc" / Int32ul,
+ "unk_87c" / Int32sl,
+ "unk_880" / Int32ul,
+ "unk_884" / Int32ul,
+ "pad_888" / Int32ul,
+ "unk_88c" / Int32ul,
+ "pad_890" / Int32ul,
+ "unk_894" / Float32l,
+ "pad_898" / Int32ul,
+ "fast_die0_ki_dt" / Float32l,
+ "pad_8a0" / Int32ul,
+ "unk_8a4" / Int32ul,
+ "unk_8a8" / Float32l,
+ "fast_die0_kp" / Float32l,
+ "pad_8b0" / Int32ul,
+ "unk_8b4" / Int32ul,
+ "pwr_min_duty_cycle_4" / Int32ul,
+ "max_pstate_scaled_8" / Dec(Int32ul),
+ "max_pstate_scaled_9" / Dec(Int32ul),
+ "fast_die0_prop_tgt_delta" / Int32ul,
+ "unk_8c8" / Int32ul,
+ "unk_8cc" / Int32ul,
+ "pad_8d0" / HexDump(Bytes(0x14)),
+ Ver("V >= V13_0B4", "unk_8e4_0" / HexDump(Bytes(0x10))),
+ "unk_8e4" / Int32ul,
+ "unk_8e8" / Int32ul,
+ "max_pstate_scaled_10" / Dec(Int32ul),
+ "unk_8f0" / Int32ul,
+ "unk_8f4" / Int32ul,
+ "pad_8f8" / Int32ul,
+ "pad_8fc" / Int32ul,
+ "unk_900" / HexDump(Bytes(0x24)),
+ "unk_924" / Array(8, Array(8, Float32l)),
+ "unk_a24" / Array(8, Array(8, Float32l)),
+ "unk_b24" / HexDump(Bytes(0x70)),
+ "max_pstate_scaled_11" / Dec(Int32ul),
+ "freq_with_off" / Int32ul,
+ "unk_b9c" / Int32ul,
+ "unk_ba0" / Int64ul,
+ "unk_ba8" / Int64ul,
+ "unk_bb0" / Int32ul,
+ "unk_bb4" / Int32ul,
+ "pad_bb8" / HexDump(Bytes(0xc2c - 0xbb8)),
+
+ "unk_c2c" / Int32ul,
+ "power_zone_count" / Int32ul,
+ "max_power_4" / Int32ul,
+ "max_power_5" / Int32ul,
+ "max_power_6" / Int32ul,
+ "unk_c40" / Int32ul,
+ "unk_c44" / Float32l,
+ "avg_power_target_filter_a_neg" / Float32l,
+ "avg_power_target_filter_a" / Float32l,
+ "avg_power_target_filter_tc_x4" / Dec(Int32ul),
+ "avg_power_target_filter_tc_xperiod" / Dec(Int32ul),
+ Ver("V >= V13_0B4", "base_clock_mhz" / Int32ul),
+ Ver("V >= V13_0B4", "unk_c58_4" / Int32ul),
+ "power_zones" / Array(5, PowerZone),
+ "avg_power_filter_tc_periods_x4" / Dec(Int32ul),
+ "unk_cfc" / Int32ul,
+ "unk_d00" / Int32ul,
+ "avg_power_filter_a_neg" / Float32l,
+ "unk_d08" / Int32ul,
+ "avg_power_filter_a" / Float32l,
+ "unk_d10" / Int32ul,
+ "avg_power_ki_dt" / Float32l,
+ "unk_d18" / Int32ul,
+ "unk_d1c" / Int32ul,
+ "unk_d20" / Float32l,
+ "avg_power_kp" / Float32l,
+ "unk_d28" / Int32ul,
+ "unk_d2c" / Int32ul,
+ "avg_power_min_duty_cycle" / Int32ul,
+ "max_pstate_scaled_12" / Int32ul,
+ "max_pstate_scaled_13" / Int32ul,
+ "unk_d3c" / Int32ul,
+ "max_power_7" / Float32l,
+ "max_power_8" / Int32ul,
+ "unk_d48" / Int32ul,
+ "unk_d4c" / Int32ul,
+ "unk_d50" / Int32ul,
+ Ver("V >= V13_0B4", "base_clock_mhz_2" / Int32ul),
+ Ver("V >= V13_0B4", "unk_d54_4" / HexDump(Bytes(0xc))),
+ "unk_d54" / HexDump(Bytes(0x10)),
+ "max_pstate_scaled_14" / Int32ul,
+ "unk_d68" / Bytes(0x24),
+
+ "t81xx_data" / AGXHWDataT81xx,
+
+ "unk_dd0" / HexDump(Bytes(0x40)),
+ Ver("V >= V13_0B4", "unk_e10_0" / AGXHWDataA130Extra),
+ "unk_e10" / HexDump(Bytes(0xc)),
+ "fast_die0_sensor_mask64_2" / Int64ul,
+ "unk_e24" / Int32ul,
+ "unk_e28" / Int32ul,
+ "unk_e2c" / HexDump(Bytes(0x1c)),
+ "unk_e48" / Array(8, Array(8, Float32l)),
+ "unk_f48" / Array(8, Array(8, Float32l)),
+ "pad_1048" / HexDump(Bytes(0x5e4)),
+ "fast_die0_sensor_mask64_alt" / Int64ul,
+ "fast_die0_sensor_present" / Int32ul,
+ Ver("V < V13_0B4", "unk_1638" / Array(2, Int32ul)),
+ "unk_1640" / HexDump(Bytes(0x2000)),
+ "unk_3640" / Int32ul,
+ "hws1" / AGXHWDataShared1,
+ Ver("V >= V13_0B4", "unk_pad1" / HexDump(Bytes(0x20))),
+ "hws2" / AGXHWDataShared2,
+ "unk_3c04" / Int32ul,
+ "hws3" / AGXHWDataShared3,
+ "unk_3c58" / HexDump(Bytes(0x3c)),
+ "unk_3c94" / Int32ul,
+ "unk_3c98" / Int64ul,
+ "unk_3ca0" / Int64ul,
+ "unk_3ca8" / Int64ul,
+ "unk_3cb0" / Int64ul,
+ "ts_last_idle" / Int64ul,
+ "ts_last_poweron" / Int64ul,
+ "ts_last_poweroff" / Int64ul,
+ "unk_3cd0" / Int64ul,
+ "unk_3cd8" / Int64ul,
+ Ver("V >= V13_0B4", "unk_3ce0_0" / Int32ul),
+ "unk_3ce0" / Int32ul,
+ "unk_3ce4" / Int32ul,
+ "unk_3ce8" / Int32ul,
+ "unk_3cec" / Int32ul,
+ "unk_3cf0" / Int32ul,
+ "unk_3cf4" / Array(8, Float32l),
+ "unk_3d14" / Array(8, Float32l),
+ "unk_3d34" / HexDump(Bytes(0x38)),
+ Ver("V >= V13_0B4", "unk_3d6c" / HexDump(Bytes(0x38))),
+ )
+
+ def __init__(self, sgx, chip_info):
+ super().__init__()
+
+ base_clock_khz = 24000
+ base_clock_mhz = base_clock_khz * 1000
+ period_ms = sgx.gpu_power_sample_period
+
+ self.unk_0 = 0
+ self.clocks_per_period = base_clock_khz * period_ms
+ self.clocks_per_period_2 = base_clock_khz * period_ms
+ self.unk_8 = 0
+ self.pwr_status = 4
+ self.unk_10 = 1.0
+ self.unk_14 = 0
+ self.unk_18 = 0
+ self.unk_1c = 0
+ self.unk_20 = 0
+ self.unk_24 = 0
+ self.actual_pstate = 1
+ self.tgt_pstate = 1
+ self.unk_30 = 0
+ self.cur_pstate = 0
+ self.unk_38 = 0
+ self.unk_3c_0 = 0
+ self.base_pstate_scaled = 100 * sgx.getprop("gpu-perf-base-pstate", 3)
+ self.unk_40 = 1
+ self.max_pstate_scaled = 100 * sgx.gpu_num_perf_states
+ self.unk_48 = 0
+ self.min_pstate_scaled = 100
+ self.freq_mhz = 0.0
+ self.unk_54 = bytes(0x20)
+ self.unk_74_0 = 0
+ # perf related
+ self.unk_74 = [0] * 16
+
+ self.unk_b4 = bytes(0x100)
+ self.unk_1b4 = 0
+ self.temp_c = 0
+ self.avg_power_mw = 0
+ self.update_ts = 0
+ self.unk_1c8 = 0
+ self.unk_1cc = bytes(0x644 - 0x1cc)
+
+ self.pad_644 = bytes(8)
+
+ self.unk_64c = 625
+ self.unk_650 = 0
+ self.pad_654 = 0
+ self.pwr_filter_a_neg = 1 - 1 / sgx.getprop("gpu-pwr-filter-time-constant", 313)
+ self.pad_65c = 0
+ self.pwr_filter_a = 1 - self.pwr_filter_a_neg
+ self.pad_664 = 0
+ self.pwr_integral_gain = sgx.getprop("gpu-pwr-integral-gain", 0.0202129)
+ self.pad_66c = 0
+ self.pwr_integral_min_clamp = sgx.getprop("gpu-pwr-integral-min-clamp", 0)
+ self.max_power_1 = chip_info.max_power
+ self.pwr_proportional_gain = sgx.getprop("gpu-pwr-proportional-gain", 5.2831855)
+ self.pad_67c = 0
+ self.pwr_pstate_related_k = -self.max_pstate_scaled / chip_info.max_power
+ self.pwr_pstate_max_dc_offset = sgx.gpu_pwr_min_duty_cycle - self.max_pstate_scaled
+ self.unk_688 = 0
+ self.max_pstate_scaled_2 = self.max_pstate_scaled
+ self.pad_690 = 0
+ self.unk_694 = 0
+ self.max_power_2 = chip_info.max_power
+ self.pad_69c = bytes(0x18)
+ self.unk_6b4 = 0
+ self.unk_6b8_0 = bytes(0x10)
+ self.max_pstate_scaled_3 = self.max_pstate_scaled
+ self.unk_6bc = 0
+ self.pad_6c0 = bytes(0x14)
+
+ # Note: integer rounding here
+ ppm_filter_tc_periods = sgx.gpu_ppm_filter_time_constant_ms // period_ms
+ self.ppm_filter_tc_periods_x4 = ppm_filter_tc_periods * 4
+ self.unk_6d8 = 0
+ self.pad_6dc = 0
+ self.ppm_filter_a_neg = 1 - 1 / ppm_filter_tc_periods
+ self.pad_6e4 = 0
+ self.ppm_filter_a = 1 - self.ppm_filter_a_neg
+ self.pad_6ec = 0
+ self.ppm_ki_dt = sgx.gpu_ppm_ki * (period_ms / 1000)
+ self.pad_6f4 = 0
+ self.pwr_integral_min_clamp_2 = self.pwr_integral_min_clamp
+ if Ver.check("V >= V13_0B4") or chip_info.chip_id != 0x8103:
+ self.unk_6fc = 65536.0
+ else:
+ self.unk_6fc = 0
+ self.ppm_kp = sgx.gpu_ppm_kp
+ self.pad_704 = 0
+ self.unk_708 = 0
+ self.pwr_min_duty_cycle = sgx.gpu_pwr_min_duty_cycle
+ self.max_pstate_scaled_4 = self.max_pstate_scaled
+ self.unk_714 = 0
+ self.pad_718 = 0
+ self.unk_71c = 0.0
+ self.max_power_3 = chip_info.max_power
+ self.cur_power_mw_2 = 0x0
+ self.ppm_filter_tc_ms = sgx.gpu_ppm_filter_time_constant_ms
+ self.unk_72c = 0
+ self.unk_730_0 = 0x232800
+ self.unk_730_4 = 0
+ self.unk_730_8 = 0
+ self.unk_730_c = 0
+ self.unk_730 = 0.0
+ self.unk_734 = 0
+ self.unk_738 = 0
+ self.unk_73c = 0
+ self.unk_740 = 0
+ self.unk_744 = 0
+ self.unk_748 = [0.0, 0.0, 0.0, 0.0]
+ self.unk_758 = 0
+ self.perf_tgt_utilization = sgx.gpu_perf_tgt_utilization
+ self.pad_760 = 0
+ self.perf_boost_min_util = sgx.getprop("gpu-perf-boost-min-util", 100)
+
+ self.perf_boost_ce_step = sgx.getprop("gpu-perf-boost-ce-step", 25)
+ self.perf_reset_iters = sgx.getprop("gpu-perf-reset-iters", 6)
+ self.pad_770 = 0x0
+ self.unk_774 = 6
+ self.unk_778 = 1
+ self.perf_filter_drop_threshold = sgx.gpu_perf_filter_drop_threshold
+ self.perf_filter_a_neg = 1 - 1 / sgx.gpu_perf_filter_time_constant
+ self.perf_filter_a2_neg = 1 - 1 / sgx.gpu_perf_filter_time_constant2
+ self.perf_filter_a = 1 - self.perf_filter_a_neg
+ self.perf_filter_a2 = 1 - self.perf_filter_a2_neg
+ self.perf_ki = sgx.getprop("gpu-perf-integral-gain", 7.895683288574219)
+ self.perf_ki2 = sgx.gpu_perf_integral_gain2
+ self.perf_integral_min_clamp = sgx.gpu_perf_integral_min_clamp
+ self.unk_79c = 95.0
+ self.perf_kp = sgx.getprop("gpu-perf-proportional-gain", 14.707962989807129)
+ self.perf_kp2 = sgx.gpu_perf_proportional_gain2
+ base_state = sgx.getprop("gpu-perf-base-pstate", 3)
+ max_state = sgx.gpu_num_perf_states
+ boost_states = max_state - base_state
+ self.boost_state_unk_k = boost_states / 0.95
+ self.base_pstate_scaled_2 = 100 * sgx.getprop("gpu-perf-base-pstate", 3)
+ self.max_pstate_scaled_5 = self.max_pstate_scaled
+ self.base_pstate_scaled_3 = 100 * sgx.getprop("gpu-perf-base-pstate", 3)
+ self.pad_7b8 = 0x0
+ self.perf_cur_utilization = 0.0
+ self.perf_tgt_utilization_2 = sgx.gpu_perf_tgt_utilization
+ self.pad_7c4 = bytes(0x18)
+ self.unk_7dc = 0x0
+ self.unk_7e0_0 = bytes(0x10)
+ self.base_pstate_scaled_4 = 100 * sgx.getprop("gpu-perf-base-pstate", 3)
+ self.pad_7e4 = 0x0
+ self.unk_7e8 = bytes(0x14)
+ self.unk_7fc = 65536.0
+ self.pwr_min_duty_cycle_2 = sgx.gpu_pwr_min_duty_cycle
+ self.max_pstate_scaled_6 = self.max_pstate_scaled
+ self.max_freq_mhz = sgx.perf_states[sgx.gpu_num_perf_states].freq // 1000000
+ self.pad_80c = 0x0
+ self.unk_810 = 0x0
+ self.pad_814 = 0x0
+ self.pwr_min_duty_cycle_3 = sgx.gpu_pwr_min_duty_cycle
+ self.unk_81c = 0x0
+ self.pad_820 = 0x0
+ self.min_pstate_scaled_4 = 100.0
+ self.max_pstate_scaled_7 = self.max_pstate_scaled
+ self.unk_82c = 0x0
+ self.unk_alpha_neg = 0.8
+ self.unk_alpha = 1 - self.unk_alpha_neg
+ self.unk_838 = 0x0
+ self.unk_83c = 0x0
+ self.pad_840 = bytes(0x2c)
+ self.unk_86c = 0x0
+ self.fast_die0_sensor_mask64 = chip_info.gpu_fast_die0_sensor_mask64
+ self.fast_die0_release_temp_cc = 100 * sgx.getprop("gpu-fast-die0-release-temp", 80)
+ self.unk_87c = chip_info.unk_87c
+ self.unk_880 = 0x4
+ self.unk_884 = 0x0
+ self.pad_888 = 0x0
+ self.unk_88c = 0x0
+ self.pad_890 = 0x0
+ self.unk_894 = 1.0
+ self.pad_898 = 0x0
+ self.fast_die0_ki_dt = sgx.gpu_fast_die0_integral_gain * (period_ms / 1000)
+ self.pad_8a0 = 0x0
+ self.unk_8a4 = 0x0
+ self.unk_8a8 = 65536.0
+ self.fast_die0_kp = sgx.gpu_fast_die0_proportional_gain
+ self.pad_8b0 = 0x0
+ self.unk_8b4 = 0x0
+ self.pwr_min_duty_cycle_4 = sgx.gpu_pwr_min_duty_cycle
+ self.max_pstate_scaled_8 = self.max_pstate_scaled
+ self.max_pstate_scaled_9 = self.max_pstate_scaled
+ self.fast_die0_prop_tgt_delta = 100 * sgx.getprop("gpu-fast-die0-prop-tgt-delta", 0)
+ self.unk_8c8 = 0
+ self.unk_8cc = chip_info.unk_8cc
+ self.pad_8d0 = bytes(0x14)
+ self.unk_8e4_0 = bytes(0x10)
+ self.unk_8e4 = 0
+ self.unk_8e8 = 0
+ self.max_pstate_scaled_10 = self.max_pstate_scaled
+ self.unk_8f0 = 0
+ self.unk_8f4 = 0
+ self.pad_8f8 = 0
+ self.pad_8fc = 0
+ self.unk_900 = bytes(0x24)
+ self.unk_924 = chip_info.unk_924
+ self.unk_a24 = chip_info.unk_924
+ self.unk_b24 = bytes(0x70)
+ self.max_pstate_scaled_11 = self.max_pstate_scaled
+ self.freq_with_off = 0x0
+ self.unk_b9c = 0
+ self.unk_ba0 = 0
+ self.unk_ba8 = 0
+ self.unk_bb0 = 0
+ self.unk_bb4 = 0
+ self.pad_bb8 = bytes(0x74)
+ self.unk_c2c = 1
+
+ self.power_zones = [PowerZone()] * 5
+ power_zone_count = 0
+ for i in range(5):
+ if sgx.getprop(f"gpu-power-zone-target-{i}", None) is None:
+ break
+ self.power_zones[i] = PowerZone(
+ sgx.getprop(f"gpu-power-zone-filter-tc-{i}", None),
+ sgx.getprop(f"gpu-power-zone-target-{i}", None),
+ sgx.getprop(f"gpu-power-zone-target-offset-{i}", None),
+ period_ms
+ )
+ power_zone_count += 1
+
+ self.power_zone_count = power_zone_count
+ self.max_power_4 = chip_info.max_power
+ self.max_power_5 = chip_info.max_power
+ self.max_power_6 = chip_info.max_power
+ self.unk_c40 = 0
+ self.unk_c44 = 0.0
+ self.avg_power_target_filter_a_neg = 1 - 1 / sgx.gpu_avg_power_target_filter_tc
+ self.avg_power_target_filter_a = 1 / sgx.gpu_avg_power_target_filter_tc
+ self.avg_power_target_filter_tc_x4 = 4 * sgx.gpu_avg_power_target_filter_tc
+ self.avg_power_target_filter_tc_xperiod = period_ms * sgx.gpu_avg_power_target_filter_tc
+ self.base_clock_mhz = base_clock_mhz
+ self.unk_c58_4 = 0
+
+ # Note: integer rounding
+ avg_power_filter_tc_periods = sgx.gpu_avg_power_filter_tc_ms // period_ms
+ self.avg_power_filter_tc_periods_x4 = avg_power_filter_tc_periods * 4
+ self.unk_cfc = 0
+ self.unk_d00 = 0
+ self.avg_power_filter_a_neg = 1 - 1 / avg_power_filter_tc_periods
+ self.unk_d08 = 0
+ self.avg_power_filter_a = 1 - self.avg_power_filter_a_neg
+ self.unk_d10 = 0
+ self.avg_power_ki_dt = sgx.gpu_avg_power_ki_only * (period_ms / 1000)
+ self.unk_d18 = 0
+ self.unk_d1c = 0
+ self.unk_d20 = 65536.0
+ self.avg_power_kp = sgx.gpu_avg_power_kp
+ self.unk_d28 = 0
+ self.unk_d2c = 0
+ self.avg_power_min_duty_cycle = sgx.gpu_avg_power_min_duty_cycle
+ self.max_pstate_scaled_12 = self.max_pstate_scaled
+ self.max_pstate_scaled_13 = self.max_pstate_scaled
+ self.unk_d3c = 0
+ self.max_power_7 = chip_info.max_power
+ self.max_power_8 = chip_info.max_power
+ self.unk_d48 = 0
+ self.unk_d4c = sgx.gpu_avg_power_filter_tc_ms
+ self.unk_d50 = 0
+ self.base_clock_mhz_2 = base_clock_mhz
+ self.unk_d54_4 = bytes(0xc)
+ self.unk_d54 = bytes(0x10)
+ self.max_pstate_scaled_14 = self.max_pstate_scaled
+ self.unk_d68 = bytes(0x24)
+
+ self.t81xx_data = AGXHWDataT81xx(sgx, chip_info)
+
+ self.unk_dd0 = bytes(0x40)
+
+ self.unk_e10_0 = AGXHWDataA130Extra()
+ self.unk_e10 = bytes(0xc)
+ self.fast_die0_sensor_mask64_2 = chip_info.gpu_fast_die0_sensor_mask64
+ self.unk_e24 = chip_info.unk_e24
+ self.unk_e28 = 1
+ self.unk_e2c = bytes(0x1c)
+ self.unk_e48 = chip_info.unk_e48
+ self.unk_f48 = chip_info.unk_e48
+ self.pad_1048 = bytes(0x5e4)
+ self.fast_die0_sensor_mask64_alt = chip_info.gpu_fast_die0_sensor_mask64_alt
+ self.fast_die0_sensor_present = chip_info.gpu_fast_die0_sensor_present
+ self.unk_1638 = [0, 1]
+ self.unk_1640 = bytes(0x2000)
+ self.unk_3640 = 0
+ self.hws1 = AGXHWDataShared1(chip_info)
+ self.unk_pad1 = bytes(0x20)
+ self.hws2 = AGXHWDataShared2(chip_info)
+ self.unk_3c04 = 0
+ self.hws3 = AGXHWDataShared3(chip_info)
+ self.unk_3c58 = bytes(0x3c)
+ self.unk_3c94 = 0 # flag
+ self.unk_3c98 = 0 # timestamp?
+ self.unk_3ca0 = 0 # timestamp?
+ self.unk_3ca8 = 0
+ self.unk_3cb0 = 0
+ self.ts_last_idle = 0
+ self.ts_last_poweron = 0
+ self.ts_last_poweroff = 0
+ self.unk_3cd0 = 0
+ self.unk_3cd8 = 0
+ self.unk_3ce0_0 = 0
+
+ self.unk_3ce0 = 0
+ self.unk_3ce4 = 0
+ self.unk_3ce8 = 1
+ self.unk_3cec = 0
+ self.unk_3cf0 = 0
+ self.unk_3cf4 = chip_info.unk_3cf4
+ self.unk_3d14 = chip_info.unk_3d14
+ self.unk_3d34 = bytes(0x38)
+ self.unk_3d6c = bytes(0x38)
+
+class IOMapping(ConstructClass):
+ _MAPTYPE = {
+ 0: "RO",
+ 1: "RW",
+ }
+
+ subcon = Struct(
+ "phys_addr" / Int64ul,
+ "virt_addr" / Int64ul,
+ "size" / Int32ul,
+ "range_size" / Int32ul, # Useally the same as size, but for MCC, this is the size of a single MMC register range.
+ "readwrite" / Int64ul
+ )
+
+ def __init__(self, phys=0, addr=0, size=0, range_size=0, readwrite=0):
+ self.phys_addr = phys
+ self.virt_addr = addr
+ self.size = size
+ self.range_size = range_size
+ self.readwrite = readwrite
+
+ def __str__(self):
+ if self.virt_addr == 0:
+ return "\n<IOMapping: Invalid>"
+
+ try:
+ hv = self._stream.uat.hv
+ except AttributeError:
+ hv = None
+
+ if hv:
+ dev, range = hv.device_addr_tbl.lookup(self.phys_addr)
+ offset = self.phys_addr - range.start
+ return f"\nIO Mapping: {self._MAPTYPE.get(self.readwrite, self.readwrite)} {self.virt_addr:#x} -> " \
+ f"{dev}+{offset:#x} = {self.phys_addr:#x} ({self.size:#x} / {self.range_size:#x})"
+ else:
+ return f"\nIO Mapping: {self._MAPTYPE.get(self.readwrite, self.readwrite)} {self.virt_addr:#x} -> " \
+ f"{self.phys_addr:#x} = {self.phys_addr:#x} ({self.size:#x} / {self.range_size:#x})"
+
+
+class AGXHWDataB(ConstructClass):
+ subcon = Struct(
+ Ver("V < V13_0B4", "unk_0" / Int64ul),
+ "unk_8" / Int64ul,
+ Ver("V < V13_0B4", "unk_10" / Int64ul),
+ "unk_18" / Int64ul,
+ "unk_20" / Int64ul,
+ "unk_28" / Int64ul,
+ "unk_30" / Int64ul,
+ "unkptr_38" / Int64ul,
+ "pad_40" / HexDump(Bytes(0x20)),
+ Ver("V < V13_0B4", "yuv_matrices" / Array(15, Array(3, Array(4, Int16sl)))),
+ Ver("V >= V13_0B4", "yuv_matrices" / Array(63, Array(3, Array(4, Int16sl)))),
+ "pad_1c8" / HexDump(Bytes(8)),
+ "io_mappings" / Array(0x14, IOMapping),
+ Ver("V >= V13_0B4", "unk_450_0" / HexDump(Bytes(0x68))),
+ "chip_id" / Int32ul,
+ "unk_454" / Int32ul,
+ "unk_458" / Int32ul,
+ "unk_45c" / Int32ul,
+ "unk_460" / Int32ul,
+ "unk_464" / Int32ul,
+ "unk_468" / Int32ul,
+ "unk_46c" / Int32ul,
+ "unk_470" / Int32ul,
+ "unk_474" / Int32ul,
+ "unk_478" / Int32ul,
+ "unk_47c" / Int32ul,
+ "unk_480" / Int32ul,
+ "unk_484" / Int32ul,
+ "unk_488" / Int32ul,
+ "unk_48c" / Int32ul,
+ "base_clock_khz" / Int32ul,
+ "power_sample_period" / Int32ul,
+ "pad_498" / ZPadding(4),
+
+ "unk_49c" / Int32ul,
+ "unk_4a0" / Int32ul,
+ "unk_4a4" / Int32ul,
+ "pad_4a8" / ZPadding(4),
+
+ "unk_4ac" / Int32ul,
+ "pad_4b0" / ZPadding(8),
+
+ "unk_4b8" / Int32ul,
+ "unk_4bc" / ZPadding(4),
+
+ "unk_4c0" / Int32ul,
+ "unk_4c4" / Int32ul,
+ "unk_4c8" / Int32ul,
+ "unk_4cc" / Int32ul,
+ "unk_4d0" / Int32ul,
+ "unk_4d4" / Int32ul,
+ "unk_4d8" / ZPadding(4),
+
+ "unk_4dc" / Int32ul,
+ "unk_4e0" / Int64ul,
+ "unk_4e8" / Int32ul,
+ "unk_4ec" / Int32ul,
+ "unk_4f0" / Int32ul,
+ "unk_4f4" / Int32ul,
+ "unk_4f8" / Int32ul,
+ "unk_4fc" / Int32ul,
+ "unk_500" / Int32ul,
+ Ver("V >= V13_0B4", "unk_504_0" / Int32ul),
+ "unk_504" / Int32ul,
+ "unk_508" / Int32ul,
+ "unk_50c" / Int32ul,
+ "unk_510" / Int32ul,
+ "unk_514" / Int32ul,
+ "unk_518" / Int32ul,
+ "unk_51c" / Int32ul,
+ "unk_520" / Int32ul,
+ "unk_524" / Int32ul,
+ "unk_528" / Int32ul,
+ "unk_52c" / Int32ul,
+ "unk_530" / Int32ul,
+ "unk_534" / Int32ul,
+ "unk_538" / Int32ul,
+ Ver("V >= V13_0B4", "unk_53c_0" / Int32ul),
+ "num_frags" / Int32ul,
+ "unk_540" / Int32ul,
+ "unk_544" / Int32ul,
+ "unk_548" / Int32ul,
+ "unk_54c" / Int32ul,
+ "unk_550" / Int32ul,
+ "unk_554" / Int32ul,
+ "gpu_region_base" / Int64ul,
+ "gpu_core" / Int32ul,
+ "gpu_rev" / Int32ul,
+ "num_cores" / Int32ul,
+ "max_pstate" / Int32ul,
+ Ver("V < V13_0B4", "num_pstates" / Int32ul),
+ "frequencies" / Array(16, Dec(Int32ul)),
+ "voltages" / Array(16, Array(8, Dec(Int32ul))),
+ "voltages_sram" / Array(16, Array(8, Dec(Int32ul))),
+ "unk_9b4" / Array(16, Float32l),
+ "unk_9f4" / Array(16, Int32ul),
+ "rel_max_powers" / Array(16, Dec(Int32ul)),
+ "rel_boost_freqs" / Array(16, Dec(Int32ul)),
+ Ver("V < V13_0B4", "min_sram_volt" / Dec(Int32ul)),
+ Ver("V < V13_0B4", "unk_ab8" / Int32ul),
+ Ver("V < V13_0B4", "unk_abc" / Int32ul),
+ Ver("V < V13_0B4", "unk_ac0" / Int32ul),
+
+ Ver("V >= V13_0B4", "unk_ac4_0" / HexDump(Bytes(0x1f0))),
+
+ "pad_ac4" / ZPadding(8),
+ "unk_acc" / Int32ul,
+ "unk_ad0" / Int32ul,
+ "pad_ad4" / ZPadding(16),
+ "unk_ae4" / Array(4, Int32ul),
+ "pad_af4" / ZPadding(4),
+ "unk_af8" / Int32ul,
+ "unk_afc" / Int32ul,
+ "unk_b00" / Int32ul,
+ "unk_b04" / Int32ul,
+ "unk_b08" / Int32ul,
+ "unk_b0c" / Int32ul,
+ "unk_b10" / Int32ul,
+ "pad_b14" / ZPadding(8),
+ "unk_b1c" / Int32ul,
+ "unk_b20" / Int32ul,
+ "unk_b24" / Int32ul,
+ "unk_b28" / Int32ul,
+ "unk_b2c" / Int32ul,
+ "unk_b30" / Int32ul,
+ "unk_b34" / Int32ul,
+ Ver("V >= V13_0B4", "unk_b38_0" / Int32ul),
+ Ver("V >= V13_0B4", "unk_b38_4" / Int32ul),
+ "unk_b38" / Array(6, Int64ul),
+ "unk_b68" / Int32ul,
+ Ver("V >= V13_0B4", "unk_b6c" / HexDump(Bytes(0xd0))),
+ Ver("V >= V13_0B4", "unk_c3c" / Int32ul),
+ )
+
+ def __init__(self, sgx, chip_info):
+ # Userspace VA map related
+ self.unk_0 = 0x13_00000000
+ self.unk_8 = 0x14_00000000
+ self.unk_10 = 0x1_00000000
+ self.unk_18 = 0xffc00000
+ self.unk_20 = 0x11_00000000
+ self.unk_28 = 0x11_00000000
+ # userspace address?
+ self.unk_30 = 0x6f_ffff8000
+ self.pad_40 = bytes(0x20)
+ # unmapped?
+ self.unkptr_38 = 0xffffffa0_11800000
+ self.pad_1c8 = bytes(8)
+
+ # Note: these are rounded poorly, need to recompute.
+ self.yuv_matrices = [
+ [ # BT.601 full range -> RGB
+ [ 0x2000, -0x8, 0x2cdb, -0x2cd3],
+ [ 0x2000, -0xb00, -0x16da, 0x21da],
+ [ 0x2000, 0x38b6, 0x8, -0x38be],
+ ],
+ [ # BT.709 full range -> RGB
+ [ 0x2000, -0x1, 0x3264, -0x3263],
+ [ 0x2000, -0x5fe, -0xefb, 0x14f9],
+ [ 0x2000, 0x3b61, 0x1, -0x3b62],
+ ],
+ [ # BT.2020 full range -> RGB
+ [ 0x2000, 0x0, 0x2f30, -0x2f30],
+ [ 0x2000, -0x544, -0x1248, 0x178c],
+ [ 0x2000, 0x3c34, -0x1, -0x3c33],
+ ],
+ [ # BT.601 limited range -> RGB
+ [ 0x2568, -0x9, 0x3343, -0x37e7],
+ [ 0x2568, -0xc92, -0x1a1e, 0x2203],
+ [ 0x2568, 0x40cf, 0x9, -0x4585],
+ ],
+ [ # BT.709 limited range -> RGB
+ [ 0x2568, -0x1, 0x3997, -0x3e43],
+ [ 0x2568, -0x6d9, -0x111f, 0x134b],
+ [ 0x2568, 0x43dd, 0x1, -0x488b],
+ ],
+ [ # BT.2020 limited range -> RGB
+ [ 0x2568, 0x0, 0x35ee, -0x3a9b],
+ [ 0x2568, -0x604, -0x14e5, 0x163c],
+ [ 0x2568, 0x44ce, -0x1, -0x497a],
+ ],
+ [ # Unknown YUV->RGB
+ [ 0x24cb, 0x0, 0x2cfa, -0x3676],
+ [ 0x24cb, -0xb0a, -0x16e9, 0x1877],
+ [ 0x24cb, 0x38d9, 0x0, -0x4255],
+ ],
+ [ # null
+ [ 0x0, 0x0, 0x0, 0x0],
+ [ 0x0, 0x0, 0x0, 0x0],
+ [ 0x0, 0x0, 0x0, 0x0],
+ ],
+ [ # RGB -> BT.601 full range
+ [ 0x2645, 0x4b23, 0xe98, 0x0],
+ [-0x15a1, -0x2a5e, 0x3fff, 0x4000],
+ [ 0x4000, -0x35a2, -0xa5e, 0x4000],
+ ],
+ [ # RGB -> BT.709 full range
+ [ 0x1b37, 0x5b8c, 0x93d, 0x0],
+ [ -0xeac, -0x3155, 0x4000, 0x4000],
+ [ 0x4000, -0x3a24, -0x5dd, 0x4000],
+ ],
+ [ # RGB -> BT.2020 full range
+ [ 0x21a0, 0x56c9, 0x797, 0x0],
+ [-0x11de, -0x2e22, 0x4000, 0x4000],
+ [ 0x4000, -0x3adb, -0x526, 0x4000],
+ ],
+ [ # RGB -> BT.601 limited range
+ [ 0x20bd, 0x4047, 0xc7c, 0x800],
+ [-0x12ed, -0x2513, 0x3800, 0x4000],
+ [ 0x3800, -0x2eee, -0x912, 0x4000],
+ ],
+ [ # RGB -> BT.709 limited range
+ [ 0x1748, 0x4e51, 0x7e7, 0x800],
+ [ -0xcd6, -0x2b2a, 0x3800, 0x4000],
+ [ 0x3800, -0x32df, -0x521, 0x4000],
+ ],
+ [ # RGB -> BT.2020 limited range
+ [ 0x1cc4, 0x4a3e, 0x67e, 0x800],
+ [ -0xfa3, -0x285e, 0x3800, 0x4000],
+ [ 0x3800, -0x337f, -0x481, 0x4000],
+ ],
+ [ # Unknown (identity?)
+ [-0x8000, 0x0, 0x0, 0x0],
+ [ 0x0, -0x8000, 0x0, 0x0],
+ [ 0x0, 0x0, -0x8000, 0x0],
+ ],
+ ]
+ if Ver.check("V >= V13_0B4"):
+ self.yuv_matrices = [
+ *self.yuv_matrices[:8],
+ *(24 * [[[0,0,0,0]]*3]),
+ *self.yuv_matrices[8:],
+ *(24 * [[[0,0,0,0]]*3]),
+ ]
+
+ self.unk_450_0 = bytes(0x68)
+
+ self.chip_id = chip_info.chip_id
+ self.unk_454 = 0x1
+ self.unk_458 = 0x1
+ self.unk_45c = 0x0
+ self.unk_460 = 0x1
+ self.unk_464 = 0x1
+ self.unk_468 = 0x1
+ self.unk_46c = 0x0
+ self.unk_470 = 0x0
+ self.unk_474 = 0x0
+ self.unk_478 = 0x0
+ self.unk_47c = 0x1
+ self.unk_480 = 0x0
+ self.unk_484 = 0x1
+ self.unk_488 = 0x0
+ self.unk_48c = 0x1
+ self.base_clock_khz = 24000
+ self.power_sample_period = sgx.gpu_power_sample_period
+ self.unk_49c = 0x1
+ self.unk_4a0 = 0x1
+ self.unk_4a4 = 0x1
+ self.unk_4ac = 0x0
+ self.unk_4b8 = 0x0
+ self.unk_4c0 = 0x1f
+ self.unk_4c4 = 0x0
+ self.unk_4c8 = 0x0
+ self.unk_4cc = 0x0
+ self.unk_4d0 = 0x0
+ self.unk_4d4 = 0x0
+ self.unk_4dc = 0x0
+ self.unk_4e0 = chip_info.hwdb_4e0
+ self.unk_4e8 = 0x0
+ self.unk_4ec = 0x0
+ self.unk_4f0 = 0x1
+ self.unk_4f4 = 0x1
+ self.unk_4f8 = 0x0
+ self.unk_4fc = 0x0
+ self.unk_500 = 0x0
+ self.unk_504_0 = 0
+ self.unk_504 = 0x31
+ self.unk_508 = 0x0
+ self.unk_50c = 0x0
+ self.unk_510 = 0x0
+ self.unk_514 = 0x0
+ self.unk_518 = 0x0
+ self.unk_51c = 0x0
+ self.unk_520 = 0x0
+ self.unk_524 = 0x1 # use_secure_cache_flush
+ self.unk_528 = 0x0
+ self.unk_52c = 0x0
+ self.unk_530 = 0x0
+ self.unk_534 = chip_info.hwdb_534
+ self.unk_538 = 0x0
+ self.unk_53c_0 = 0
+ self.num_frags = chip_info.num_cores
+ self.unk_540 = 0x0
+ self.unk_544 = 0x0
+ self.unk_548 = 0x0
+ self.unk_54c = 0x0
+ self.unk_550 = 0x0
+ self.unk_554 = 0x1
+ self.gpu_region_base = sgx.gpu_region_base
+ self.gpu_core = chip_info.gpu_core
+ self.gpu_rev = chip_info.gpu_rev
+ self.num_cores = chip_info.num_cores
+ self.max_pstate = sgx.gpu_num_perf_states
+ self.num_pstates = sgx.gpu_num_perf_states + 1
+
+ self.frequencies = [0] * 16
+ self.voltages = [[0] * 8 for i in range(16)]
+ self.voltages_sram = [[0] * 8 for i in range(16)]
+ self.unk_9b4 = [0.] * 16
+ self.unk_9f4 = [0] * 16
+ self.rel_max_powers = [0] * 16
+ self.rel_boost_freqs = [0] * 16
+ self.min_sram_volt = chip_info.min_sram_volt
+ self.unk_ab8 = chip_info.hwdb_ab8
+ self.unk_abc = chip_info.hwdb_abc
+ self.unk_ac0 = 0x1020
+ self.unk_ac4_0 = bytes(0x1f0)
+ self.unk_acc = 0x0
+ self.unk_ad0 = 0x0
+ if Ver.check("V >= V13_0B4"):
+ self.unk_ae4 = [0x0, 0x3, 0x7, 0x7]
+ else:
+ self.unk_ae4 = [0x0, 0xf, 0x3f, 0x3f]
+ self.unk_af8 = 0x0
+ self.unk_afc = 0x0
+ self.unk_b00 = 0x0
+ self.unk_b04 = 0x0
+ self.unk_b08 = 0x0
+ self.unk_b0c = 0x0
+ self.unk_b10 = 0x1
+ self.unk_b1c = 0x0
+ self.unk_b20 = 0x0
+ self.unk_b24 = 0x1
+ self.unk_b28 = 0x1
+ self.unk_b2c = 0x1
+ self.unk_b30 = chip_info.hwdb_b30
+ self.unk_b34 = 0x0
+ self.unk_b38_0 = 1
+ self.unk_b38_4 = 1
+ self.unk_b38 = [0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff]
+ self.unk_b68 = 0x0
+ self.unk_b6c = bytes(0xd0)
+ self.unk_c3c = 0x19
+
+class InitData_BufferMgrCtl(ConstructValueClass):
+ subcon = Array(126, Bytes(0x10))
+
+ def __init__(self):
+ self.value = [bytes(0x10)] * 126
+
+class InitData_GPUQueueStatsTA(ConstructClass):
+ subcon = Struct(
+ "busy" / Int32ul,
+ "unk_4" / Int32ul,
+ "cur_cmdqueue" / Int64ul,
+ "cur_count" / Int32ul,
+ "unk_14" / Int32ul,
+ )
+ def __init__(self):
+ self.busy = 0
+ self.unk_4 = 0
+ self.cur_cmdqueue = 0
+ self.cur_count = 0
+ self.unk_14 = 0
+
+class InitData_GPUStatsTA(ConstructClass):
+ subcon = Struct(
+ "unk_4" / Int32ul,
+ "queues" / Array(4, InitData_GPUQueueStatsTA),
+ "unk_68" / Bytes(0x8),
+ "unk_70" / Int32ul,
+ "unk_74" / Int32ul,
+ "unk_timestamp" / Int64ul,
+ "unk_80" / HexDump(Bytes(0x40)),
+ Ver("V >= V13_0B4", "unk_c0" / HexDump(Bytes(0x800))),
+ )
+
+ def __init__(self):
+ self.unk_4 = 0
+ self.queues = [InitData_GPUQueueStatsTA() for i in range(4)]
+ self.unk_68 = bytes(0x8)
+ self.unk_70 = 0
+ self.unk_74 = 0
+ self.unk_timestamp = 0
+ self.unk_80 = bytes(0x40)
+ self.unk_c0 = bytes(0x800)
+
+class InitData_GPUQueueStats3D(ConstructClass):
+ subcon = Struct(
+ "busy" / Int32ul,
+ "cur_cmdqueue" / Int64ul,
+ "unk_c" / Int32ul,
+ "unk_10" / Int32ul,
+ "unk_14" / HexDump(Bytes(0x28 - 0x14)),
+ )
+ def __init__(self):
+ self.busy = 0
+ self.cur_cmdqueue = 0
+ self.unk_c = 0
+ self.unk_10 = 0
+ self.unk_14 = bytes(0x14)
+
+class InitData_GPUStats3D(ConstructClass):
+ subcon = Struct(
+ "unk_0" / Bytes(0x18),
+ "queues" / Array(4, InitData_GPUQueueStats3D),
+ "unk_d0" / HexDump(Bytes(0x38)),
+ "tvb_overflows_1" / Int32ul,
+ "tvb_overflows_2" / Int32ul,
+ "unk_f8" / Int32ul,
+ "unk_fc" / Int32ul,
+ "cur_stamp_id" / Int32sl,
+ "unk_104" / Bytes(0x14),
+ "unk_118" / Int32sl,
+ "unk_11c" / Int32ul,
+ "unk_120" / Int32ul,
+ "unk_124" / Int32ul,
+ "unk_128" / Int32ul,
+ "unk_12c" / Int32ul,
+ "unk_timestamp" / Int64ul,
+ "unk_134" / Bytes(0x1c0 - 0x134),
+ Ver("V >= V13_0B4", "unk_1c0" / HexDump(Bytes(0x800))),
+ )
+
+ def __init__(self):
+ self.unk_0 = bytes(0x18)
+ self.queues = [InitData_GPUQueueStats3D() for i in range(4)]
+ self.unk_68 = 0
+ self.cur_cmdqueue = 0
+ self.unk_d0 = bytes(0x38)
+ self.tvb_overflows_1 = 0
+ self.tvb_overflows_2 = 0
+ self.unk_f8 = 0
+ self.unk_fc = 0
+ self.cur_stamp_id = -1
+ self.unk_104 = bytes(0x14)
+ self.unk_118 = -1
+ self.unk_11c = 0
+ self.unk_120 = 0
+ self.unk_124 = 0
+ self.unk_128 = 0
+ self.unk_12c = 0
+ self.unk_timestamp = 0
+ self.unk_134 = bytes(0x1c0 - 0x134)
+ self.unk_1c0 = bytes(0x800)
+
+class InitData_GPUGlobalStatsTA(ConstructClass):
+ subcon = Struct(
+ "total_cmds" / Int32ul,
+ "stats" / InitData_GPUStatsTA,
+ )
+
+ def __init__(self):
+ self.total_cmds = 0
+ self.stats = InitData_GPUStatsTA()
+
+class InitData_GPUGlobalStats3D(ConstructClass):
+ subcon = Struct(
+ "total_cmds" / Int32ul,
+ "unk_4" / Int32ul,
+ "stats" / InitData_GPUStats3D,
+ )
+
+ def __init__(self):
+ self.total_cmds = 0
+ self.unk_4 = 0
+ self.stats = InitData_GPUStats3D()
+
+class InitData_RegionB(ConstructClass):
+ subcon = Struct(
+ "channels" / ChannelInfoSet,
+ "pad_110" / ZPadding(0x50),
+ "unk_160" / Default(Int64ul, 0),
+ "unk_168" / Default(Int64ul, 0),
+ "stats_ta_addr" / Int64ul,
+ "stats_ta" / ROPointer(this.stats_ta_addr, InitData_GPUGlobalStatsTA),
+ "stats_3d_addr" / Int64ul,
+ "stats_3d" / ROPointer(this.stats_3d_addr, InitData_GPUGlobalStats3D),
+ "stats_cp_addr" / Int64ul,
+ "stats_cp" / ROPointer(this.stats_cp_addr, Bytes(0x140)),
+ "hwdata_a_addr" / Int64ul,
+ "hwdata_a" / ROPointer(this.hwdata_a_addr, AGXHWDataA),
+ "unkptr_190" / Int64ul, # size: 0x80, empty
+ "unk_190" / ROPointer(this.unkptr_190, Bytes(0x80)),
+ "unkptr_198" / Int64ul, # size: 0xc0, fw writes timestamps into this
+ "unk_198" / ROPointer(this.unkptr_198, Bytes(0xc0)),
+ "hwdata_b_addr" / Int64ul, # size: 0xb80, io stuff
+ "hwdata_b" / ROPointer(this.hwdata_b_addr, AGXHWDataB),
+ "hwdata_b_addr2" / Int64ul, # repeat of 1a0
+ "fwlog_ring2" / Int64ul, #
+ "unkptr_1b8" / Int64ul, # Unallocated, Size 0x1000
+ "unk_1b8" / Lazy(ROPointer(this.unkptr_1b8, Bytes(0x1000))),
+ "unkptr_1c0" / Int64ul, # Unallocated, size 0x300
+ "unk_1c0" / Lazy(ROPointer(this.unkptr_1c0, Bytes(0x300))),
+ "unkptr_1c8" / Int64ul, # Unallocated, unknown size
+ "unk_1c8" / Lazy(ROPointer(this.unkptr_1c8, Bytes(0x1000))),
+ "unk_1d0" / Int32ul,
+ "unk_1d4" / Int32ul,
+ "unk_1d8" / HexDump(Bytes(0x3c)),
+ "buffer_mgr_ctl_addr" / Int64ul, # Size: 0x4000
+ "buffer_mgr_ctl" / ROPointer(this.buffer_mgr_ctl_addr, InitData_BufferMgrCtl),
+ "buffer_mgr_ctl_addr2" / Int64ul, # Size: 0x4000
+ # Written to by DC_09
+ "unk_224" / HexDump(Bytes(0x685c)),
+ "unk_6a80" / Int32ul,
+ "gpu_idle" / Int32ul,
+ "unkpad_6a88" / HexDump(Bytes(0x14)),
+ "unk_6a9c" / Int32ul,
+ "unk_ctr0" / Int32ul,
+ "unk_ctr1" / Int32ul,
+ "unk_6aa8" / Int32ul,
+ "unk_6aac" / Int32ul,
+ "unk_ctr2" / Int32ul,
+ "unk_6ab4" / Int32ul,
+ "unk_6ab8" / Int32ul,
+ "unk_6abc" / Int32ul,
+ "unk_6ac0" / Int32ul,
+ "unk_6ac4" / Int32ul,
+ "unk_ctr3" / Int32ul,
+ "unk_6acc" / Int32ul,
+ "unk_6ad0" / Int32ul,
+ "unk_6ad4" / Int32ul,
+ "unk_6ad8" / Int32ul,
+ "unk_6adc" / Int32ul,
+ "unk_6ae0" / Int32ul,
+ "unk_6ae4" / Int32ul,
+ "unk_6ae8" / Int32ul,
+ "unk_6aec" / Int32ul,
+ "unk_6af0" / Int32ul,
+ "unk_ctr4" / Int32ul,
+ "unk_ctr5" / Int32ul,
+ "unk_6afc" / Int32ul,
+ "pad_6b00" / HexDump(Bytes(0x38)),
+ "unk_6b38" / Int32ul,
+ "pad_6b3c" / HexDump(Bytes(0x84)),
+ )
+
+ def __init__(self):
+ super().__init__()
+ self.unk_1d0 = 0
+ self.unk_1d4 = 0
+ self.unk_1d8 = bytes(0x3c)
+ self.unk_224 = bytes(0x685c)
+ self.unkpad_6a88 = bytes(0x14)
+ self.pad_6b00 = bytes(0x38)
+ self.unk_6b38 = 0xff
+ self.pad_6b3c = bytes(0x84)
+
+ def mon(self, add_fn):
+ add_fn(self.unkptr_170, 0x140, "unkptr_170")
+ add_fn(self.unkptr_178, 0x1c0, "unkptr_178")
+ add_fn(self.unkptr_180, 0x140, "unkptr_180")
+ add_fn(self.unkptr_188_addr, 0x3b80, "unkptr_188")
+ add_fn(self.unkptr_190, 0x80, "unkptr_190")
+ add_fn(self.unkptr_198_addr, 0xc0, "unkptr_198")
+ add_fn(self.unkptr_1a0_addr, 0xb80, "unkptr_1a0")
+ add_fn(self.fwlog_ring2, 0x51000, "fwlog_ring2")
+ add_fn(self.unkptr_214, 0x4000, "unkptr_214")
+
+ # Unallocated during init
+ #add_fn(self.unkptr_1b8, 0x1000, "unkptr_1b8")
+ #add_fn(self.unkptr_1c0, 0x300, "unkptr_1c0")
+ #add_fn(self.unkptr_1c8, 0x1000, "unkptr_1c8")
+
+class InitData_PendingStamp(ConstructClass):
+ subcon = Struct(
+ "info" / Int32ul,
+ "wait_value" / Int32ul,
+ )
+
+ def __init__(self):
+ super().__init__()
+ self.info = 0
+ self.wait_value = 0
+
+ def __bool__(self):
+ return bool(self.info or self.wait_value)
+
+class InitData_FaultInfo(ConstructClass):
+ subcon = Struct(
+ "unk_0" / Int32ul,
+ "unk_4" / Int32ul,
+ "queue_uuid" / Int32ul,
+ "unk_c" / Int32ul,
+ "unk_10" / Int32ul,
+ "unk_14" / Int32ul,
+ )
+
+ def __init__(self):
+ super().__init__()
+ self.unk_0 = 0
+ self.unk_4 = 0
+ self.queue_uuid = 0
+ self.unk_c = 0
+ self.unk_10 = 0
+ self.unk_14 = 0
+
+class RCPowerZone(ConstructClass):
+ subcon = Struct(
+ "target" / Dec(Int32ul),
+ "target_off" / Dec(Int32ul),
+ "tc" / Dec(Int32ul),
+ )
+ def __init__(self, tc=None, target=None, off=None):
+ if tc is None:
+ self.target = 0
+ self.target_off = 0
+ self.tc = 0
+ else:
+ self.target = target
+ self.target_off = self.target - off
+ self.tc = tc
+
+
+class InitData_RegionC(ConstructClass):
+ subcon = Struct(
+ "ktrace_enable" / Int32ul,
+ "unk_4" / HexDump(Bytes(0x24)),
+ Ver("V >= V13_0B4", "unk_28_0" / Int32ul),
+ "unk_28" / Int32ul,
+ Ver("V >= V13_0B4", "unk_2c_0" / Int32ul),
+ "unk_2c" / Int32ul,
+ "unk_30" / Int32ul,
+ "unk_34" / Int32ul,
+ "unk_38" / HexDump(Bytes(0x1c)),
+ "unk_54" / Int16ul,
+ "unk_56" / Int16ul,
+ "unk_58" / Int16ul,
+ "unk_5a" / Int32ul,
+ "unk_5e" / Int32ul,
+ "unk_62" / Int32ul,
+ Ver("V >= V13_0B4", "unk_66_0" / HexDump(Bytes(0xc))),
+ "unk_66" / Int32ul,
+ "unk_6a" / HexDump(Bytes(0x16)),
+ "unk_80" / HexDump(Bytes(0xf80)),
+ "unk_1000" / HexDump(Bytes(0x7000)),
+ "unk_8000" / HexDump(Bytes(0x900)),
+ Ver("V >= V13_0B4", "unk_8900_0" / Int32ul),
+ "unk_8900" / Int32ul,
+ "unk_atomic" / Int32ul,
+ "max_power" / Int32ul,
+ "max_pstate_scaled" / Int32ul,
+ "max_pstate_scaled_2" / Int32ul,
+ "unk_8914" / Int32ul,
+ "unk_8918" / Int32ul,
+ "max_pstate_scaled_3" / Int32ul,
+ "unk_8920" / Int32ul,
+ "power_zone_count" / Int32ul,
+ "avg_power_filter_tc_periods" / Int32ul,
+ "avg_power_ki_dt" / Float32l,
+ "avg_power_kp" / Float32l,
+ "avg_power_min_duty_cycle" / Int32ul,
+ "avg_power_target_filter_tc" / Int32ul,
+ "power_zones" / Array(5, RCPowerZone),
+ "unk_8978" / HexDump(Bytes(0x44)),
+ Ver("V >= V13_0B4", "unk_89bc_0" / HexDump(Bytes(0x3c))),
+ "unk_89bc" / Int32ul,
+ "fast_die0_release_temp" / Int32ul,
+ "unk_89c4" / Int32sl,
+ "fast_die0_prop_tgt_delta" / Int32ul,
+ "fast_die0_kp" / Float32l,
+ "fast_die0_ki_dt" / Float32l,
+ "unk_89d4" / HexDump(Bytes(0xc)),
+ "unk_89e0" / Int32ul,
+ "max_power_2" / Int32ul,
+ "ppm_kp" / Float32l,
+ "ppm_ki_dt" / Float32l,
+ "unk_89f0" / Int32ul,
+ Ver("V >= V13_0B4", "unk_89f4_0" / HexDump(Bytes(0x8))),
+ Ver("V >= V13_0B4", "unk_89f4_8" / Int32ul),
+ Ver("V >= V13_0B4", "unk_89f4_c" / HexDump(Bytes(0x50))),
+ "hws1" / AGXHWDataShared1,
+ "hws2" / AGXHWDataShared2,
+ "hws3" / AGXHWDataShared3,
+ "unk_9004" / HexDump(Bytes(8)),
+ Ver("V >= V13_0B4", "unk_900c_0" / HexDump(Bytes(0x28))),
+ "unk_900c" / Int32ul,
+ Ver("V >= V13_0B4", "unk_9010_0" / Int32ul),
+ Ver("V >= V13_0B4", "unk_9010_4" / HexDump(Bytes(0x14))),
+ "unk_9010" / HexDump(Bytes(0x2c)),
+ "unk_903c" / Int32ul,
+ "unk_9040" / HexDump(Bytes(0xc0)),
+ "unk_9100" / HexDump(Bytes(0x6f00)),
+ "unk_10000" / HexDump(Bytes(0xe50)),
+ "unk_10e50" / Int32ul,
+ "unk_10e54" / HexDump(Bytes(0x2c)),
+ "fault_control" / Int32ul,
+ "do_init" / Int32ul,
+ "unk_10e88" / HexDump(Bytes(0x188)),
+ "idle_ts" / Int64ul,
+ "idle_unk" / Int64ul,
+ "unk_11020" / Int32ul,
+ "unk_11024" / Int32ul,
+ "unk_11028" / Int32ul,
+ Ver("V >= V13_0B4", "unk_1102c_0" / Int32ul),
+ Ver("V >= V13_0B4", "unk_1102c_4" / Int32ul),
+ Ver("V >= V13_0B4", "unk_1102c_8" / Dec(Int32ul)),
+ Ver("V >= V13_0B4", "unk_1102c_c" / Int32ul),
+ Ver("V >= V13_0B4", "unk_1102c_10" / Int32ul),
+ "unk_1102c" / Int32ul,
+ "idle_to_off_delay_ms" / Int32ul,
+ "fender_idle_to_off_delay_ms" / Int32ul,
+ "fw_early_wake_timeout_ms" / Int32ul,
+ "pending_stamps" / Array(0x110, InitData_PendingStamp),
+ "unk_117bc" / Int32ul,
+ "fault_info" / InitData_FaultInfo,
+ "counter" / Int32ul,
+ "unk_118dc" / Int32ul,
+ Ver("V >= V13_0B4", "unk_118e0_0" / HexDump(Bytes(0x9c))),
+ "unk_118e0" / Dec(Int32ul),
+ Ver("V >= V13_0B4", "unk_118e4_0" / Dec(Int32ul)),
+ "unk_118e4" / Int32ul,
+ "unk_118e8" / Int32ul,
+ "unk_118ec" / Array(0x15, Int8ul),
+ "unk_11901" / HexDump(Bytes(0x43f)),
+ Ver("V >= V13_0B4", "unk_11d40" / HexDump(Bytes(0x19c))),
+ Ver("V >= V13_0B4", "unk_11edc" / Int32ul),
+ Ver("V >= V13_0B4", "unk_11ee0" / HexDump(Bytes(0x1c))),
+ Ver("V >= V13_0B4", "unk_11efc" / Int32ul),
+ )
+
+ def __init__(self, sgx, chip_info):
+ period_ms = sgx.gpu_power_sample_period
+ avg_power_filter_tc_periods = sgx.gpu_avg_power_filter_tc_ms // period_ms
+
+ self.ktrace_enable = 0# 0xffffffff
+ self.unk_4 = bytes(0x24)
+ self.unk_28_0 = 1 # debug
+ self.unk_28 = 1
+ self.unk_2c_0 = 0
+ self.unk_2c = 1
+ self.unk_30 = 0
+ self.unk_34 = 120
+ self.unk_38 = bytes(0x1c)
+ self.unk_54 = 0xffff
+ self.unk_56 = 40
+ self.unk_58 = 0xffff
+ self.unk_5a = 0
+ self.unk_5e = 1
+ self.unk_62 = 0
+ self.unk_66_0 = bytes(0xc)
+ self.unk_66 = 1
+ self.unk_6a = bytes(0x16)
+ self.unk_80 = bytes(0xf80)
+ self.unk_1000 = bytes(0x7000)
+ self.unk_8000 = bytes(0x900)
+ self.unk_8900_0 = 0
+ self.unk_8900 = 1
+ # Accessed with OSIncrementAtomic/OSDecrementAtomic
+ self.unk_atomic = 0
+ self.max_power = chip_info.max_power
+ self.max_pstate_scaled = 100 * sgx.gpu_num_perf_states
+ self.max_pstate_scaled_2 = 100 * sgx.gpu_num_perf_states
+ self.unk_8914 = 0
+ self.unk_8918 = 0
+ self.max_pstate_scaled_3 = 100 * sgx.gpu_num_perf_states
+ self.unk_8920 = 0
+
+ self.power_zones = [RCPowerZone()] * 5
+ power_zone_count = 0
+ for i in range(5):
+ if sgx.getprop(f"gpu-power-zone-target-{i}", None) is None:
+ break
+ self.power_zones[i] = RCPowerZone(
+ sgx.getprop(f"gpu-power-zone-filter-tc-{i}", None),
+ sgx.getprop(f"gpu-power-zone-target-{i}", None),
+ sgx.getprop(f"gpu-power-zone-target-offset-{i}", None),
+ )
+ power_zone_count += 1
+
+ self.power_zone_count = power_zone_count
+ self.avg_power_filter_tc_periods = avg_power_filter_tc_periods
+ self.avg_power_ki_dt = sgx.gpu_avg_power_ki_only * (period_ms / 1000)
+ self.avg_power_kp = sgx.gpu_avg_power_kp
+ self.avg_power_min_duty_cycle = sgx.gpu_avg_power_min_duty_cycle
+ self.avg_power_target_filter_tc = sgx.gpu_avg_power_target_filter_tc
+ self.unk_8978 = bytes(0x44)
+ self.unk_89bc_0 = bytes(0x3c)
+ self.unk_89bc = chip_info.unk_8cc
+ self.fast_die0_release_temp = 100 * sgx.getprop("gpu-fast-die0-release-temp", 80)
+ self.unk_89c4 = chip_info.unk_87c
+ self.fast_die0_prop_tgt_delta = 100 * sgx.getprop("gpu-fast-die0-prop-tgt-delta", 0)
+ self.fast_die0_kp = sgx.gpu_fast_die0_proportional_gain
+ self.fast_die0_ki_dt = sgx.gpu_fast_die0_integral_gain * (period_ms / 1000)
+ self.unk_89d4 = bytes(0xc)
+ self.unk_89e0 = 1
+ self.max_power_2 = chip_info.max_power
+ self.ppm_kp = sgx.gpu_ppm_kp
+ self.ppm_ki_dt = sgx.gpu_ppm_ki * (period_ms / 1000)
+ self.unk_89f0 = 0
+ self.unk_89f4_0 = bytes(8)
+ self.unk_89f4_8 = 1
+ self.unk_89f4_c = bytes(0x50)
+ self.hws1 = AGXHWDataShared1(chip_info)
+ self.hws2 = AGXHWDataShared2(chip_info)
+ self.hws3 = AGXHWDataShared3(chip_info)
+ self.unk_9004 = bytes(8)
+ self.unk_900c_0 = bytes(0x28)
+ self.unk_900c = 1
+ self.unk_9010_0 = 1
+ self.unk_9010_4 = bytes(0x14)
+ self.unk_9010 = bytes(0x2c)
+ if Ver.check("V >= V13_0B4"):
+ self.unk_903c = 1
+ else:
+ self.unk_903c = 0
+ self.unk_9040 = bytes(0xc0)
+ self.unk_9100 = bytes(0x6f00)
+ self.unk_10000 = bytes(0xe50)
+ self.unk_10e50 = 0
+ self.unk_10e54 = bytes(0x2c)
+ self.unk_10e80_0 = bytes(0xed4)
+ self.unk_10e80_ed0 = 0
+ self.unk_10e80_ed4 = bytes(0x2c)
+ #self.fault_control = 0xb
+ self.fault_control = 0
+ self.do_init = 1
+ self.unk_10e88 = bytes(0x188)
+ self.idle_ts = 0
+ self.idle_unk = 0
+ self.unk_11020 = 40
+ self.unk_11024 = 10
+ self.unk_11028 = 250
+ self.unk_1102c_0 = 1
+ self.unk_1102c_4 = 1
+ self.unk_1102c_8 = 100
+ self.unk_1102c_c = 1
+ self.unk_1102c_10 = 0
+ self.unk_1102c = 0
+ self.idle_to_off_delay_ms = sgx.getprop("gpu-idle-off-delay-ms", 2)
+ self.fender_idle_to_off_delay_ms = sgx.getprop("gpu-fender-idle-off-delay-ms", 40)
+ self.fw_early_wake_timeout_ms = sgx.getprop("gpu-fw-early-wake-timeout-ms", 5)
+ self.pending_stamps = [InitData_PendingStamp() for i in range(0x110)]
+ self.unk_117bc = 0
+ self.fault_info = InitData_FaultInfo()
+ self.counter = 0
+ self.unk_118dc = 0
+ self.unk_118e0_0 = bytes(0x9c)
+ self.unk_118e0 = 40
+ self.unk_118e4_0 = 50
+ self.unk_118e4 = 0
+ self.unk_118e8 = 0 if chip_info.unk_118ec is None else 1
+ self.unk_118ec = chip_info.unk_118ec or [0] * 0x15
+ self.unk_11901 = bytes(0x43f)
+
+ self.unk_11d40 = bytes(0x19c)
+ self.unk_11edc = 8
+ self.unk_11ee0 = bytes(0x1c)
+ self.unk_11efc = 8
+
+class UatLevelInfo(ConstructClass):
+ subcon = Struct(
+ "unk_3" / Int8ul, # always 8
+ "unk_1" / Int8ul, # always 14, page bits?
+ "unk_2" / Int8ul, # always 14, also page bits?
+ "index_shift" / Int8ul,
+ "num_entries" / Int16ul,
+ "unk_4" / Int16ul, # 0x4000, Table size?
+ "unk_8" / Int64ul, # always 1
+ "phys_mask" / Int64ul,
+ "index_mask" / Int64ul,
+ )
+
+ def __init__(self, index_shift, num_entries, phys_mask):
+ self.index_shift = index_shift
+ self.unk_1 = 14
+ self.unk_2 = 14
+ self.unk_3 = 8
+ self.unk_4 = 0x4000 # I doubt anything other than 16k pages is supported
+ self.num_entries = num_entries
+ self.unk_8 = 1
+ self.phys_mask = phys_mask
+ self.index_mask = (num_entries - 1) << index_shift
+
+class InitData(ConstructClass):
+ subcon = Struct(
+ Ver("V >= V13_0B4", "ver_info" / Array(4, Int16ul)),
+ "regionA_addr" / Int64ul, # allocation size: 0x4000
+ "regionA" / ROPointer(this.regionA_addr, HexDump(Bytes(0x4000))),
+ "unk_8" / Default(Int32ul, 0),
+ "unk_c"/ Default(Int32ul, 0),
+ "regionB_addr" / Int64ul, # 0xfa00c338000 allocation size: 0x6bc0
+ "regionB" / ROPointer(this.regionB_addr, InitData_RegionB),
+ "regionC_addr" / Int64ul, # 0xfa000200000 allocation size: 0x11d40, heap?
+ "regionC" / ROPointer(this.regionC_addr, InitData_RegionC),
+ "fw_status_addr" / Int64ul, # allocation size: 0x4000, but probably only 0x80 bytes long
+ "fw_status" / ROPointer(this.fw_status_addr, InitData_FWStatus),
+ "uat_page_size" / Int16ul,
+ "uat_page_bits" / Int8ul,
+ "uat_num_levels" / Int8ul,
+ "uat_level_info" / Array(3, UatLevelInfo),
+ "pad_8c" / HexDump(Default(Bytes(0x14), bytes(0x14))),
+ "host_mapped_fw_allocations" / Int32ul, # must be 1
+ "unk_ac" / Int32ul,
+ "unk_b0" / Int32ul,
+ "unk_b4" / Int32ul,
+ "unk_b8" / Int32ul,
+ )
+
+ def __init__(self):
+ super().__init__()
+ self.unk_ac = 0
+ self.unk_b0 = 0
+ self.unk_b4 = 0
+ self.unk_b8 = 0
+
+__all__.extend(k for k, v in globals().items()
+ if (callable(v) or isinstance(v, type)) and v.__module__ == __name__)
diff --git a/tools/proxyclient/m1n1/fw/agx/microsequence.py b/tools/proxyclient/m1n1/fw/agx/microsequence.py
new file mode 100644
index 0000000..475597f
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/agx/microsequence.py
@@ -0,0 +1,800 @@
+"""
+I think these are executed by a simple state machine on the firmware's arm core,
+and the typical result is a commandlist submitting to one of the gpu's hardware
+command processors.
+
+It seems like a common pattern is:
+ 1. Start (3D or Compute)
+ 2. Timestamp
+ 3. Wait For Interrupts
+ 4. Timestamp again
+ 5. Finish (3D or Compute)
+ 6. End
+
+Error messages call these as SKU commands
+
+"""
+from m1n1.constructutils import *
+
+from construct import *
+from construct.core import Int64ul, Int32ul, Int32sl
+import textwrap
+
+__all__ = []
+
+class TimeStamp(ConstructValueClass):
+ subcon = Int64ul
+
+ def __init__(self, value=0):
+ self.value = value
+
+class TsFlag(ConstructValueClass):
+ subcon = Int8ul
+
+ def __init__(self, value=0):
+ self.value = value
+
+class WrappedPointer(ConstructValueClass):
+ subcon = Int64ul
+
+ def __init__(self, value=0):
+ self.value = value
+
+class StampCounter(ConstructValueClass):
+ subcon = Hex(Int32ul)
+
+ def __init__(self):
+ self.value = 0
+
+class BufferManagerBlockControl(ConstructClass):
+ subcon = Struct(
+ "total" / Int32ul,
+ "wptr" / Int32ul,
+ "unk" / Int32ul,
+ "pad" / ZPadding(0x34)
+ )
+
+class BufferManagerCounter(ConstructClass):
+ subcon = Struct(
+ "count" / Int32ul,
+ "pad" / ZPadding(0x3c)
+ )
+
+class BufferManagerMisc(ConstructClass):
+ subcon = Struct(
+ "gpu_0" / Default(Int32ul, 0),
+ "gpu_4" / Default(Int32ul, 0),
+ "gpu_8" / Default(Int32ul, 0),
+ "gpu_c" / Default(Int32ul, 0),
+ "pad_10" / ZPadding(0x10),
+ "cpu_flag" / Int32ul,
+ "pad_24" / ZPadding(0x1c),
+ )
+
+class BufferManagerInfo(ConstructClass):
+ subcon = Struct(
+ "gpu_counter" / Int32ul,
+ "unk_4" / Int32ul,
+ "last_id" / Int32ul,
+ "cur_id" / Int32ul,
+ "unk_10" / Int32ul,
+ "gpu_counter2" / Int32ul,
+ "unk_18" / Int32ul,
+ Ver("V < V13_0B4", "unk_1c" / Int32ul),
+ "page_list_addr" / Int64ul,
+ "page_list_size" / Int32ul,
+ "page_count" / Int32ul,
+ "unk_30" / Int32ul,
+ "block_count" / Int32ul,
+ "unk_38" / Int32ul,
+ "block_list_addr" / Int64ul,
+ "block_ctl_addr" / Int64ul, # points to two u32s
+ "block_ctl" / ROPointer(this.block_ctl_addr, BufferManagerBlockControl),
+ "last_page" / Int32ul,
+ "gpu_page_ptr1" / Int32ul,
+ "gpu_page_ptr2" / Int32ul,
+ "unk_58" / Int32ul,
+ "block_size" / Int32ul,
+ "unk_60" / Int64ul,
+ "counter_addr" / Int64ul,
+ "counter" / ROPointer(this.counter_addr, BufferManagerCounter),
+ "unk_70" / Int32ul,
+ "unk_74" / Int32ul,
+ "unk_78" / Int32ul,
+ "unk_7c" / Int32ul,
+ "unk_80" / Int32ul,
+ "unk_84" / Int32ul,
+ "unk_88" / Int32ul,
+ "unk_8c" / Int32ul,
+ "unk_90" / HexDump(Bytes(0x30)),
+
+ )
+
+ def __init__(self):
+ super().__init__()
+ self.gpu_counter = 0x0
+ self.unk_4 = 0
+ self.last_id = 0x0
+ self.cur_id = 0xffffffff
+ self.unk_10 = 0x0
+ self.gpu_counter2 = 0x0
+ self.unk_18 = 0x0
+ self.unk_1c = 0x0
+ self.unk_30 = 0xd1a
+ self.unk_38 = 0x0
+ self.gpu_page_ptr1 = 0x0
+ self.gpu_page_ptr2 = 0x0
+ self.unk_58 = 0x0
+ self.unk_60 = 0x0
+ self.unk_70 = 0x0
+ self.unk_74 = 0x0
+ self.unk_78 = 0x0
+ self.unk_7c = 0x0
+ self.unk_80 = 0x1
+ self.unk_84 = 0x66cc
+ self.unk_88 = 0x2244
+ self.unk_8c = 0x0
+ self.unk_90 = bytes(0x30)
+
+
+class Start3DClearPipelineBinding(ConstructClass):
+ subcon = Struct(
+ "pipeline_bind" / Int64ul,
+ "address" / Int64ul,
+ )
+
+ def __init__(self, pipeline_bind=None, address=None):
+ super().__init__()
+ self.pipeline_bind = pipeline_bind
+ self.address = address
+
+class Start3DStorePipelineBinding(ConstructClass):
+ subcon = Struct(
+ "unk_0" / Int64ul,
+ "unk_8" / Int32ul,
+ "pipeline_bind" / Int32ul,
+ "unk_10" / Int32ul,
+ "address" / Int32ul,
+ "unk_18" / Int32ul,
+ "unk_1c_padding" / Int32ul,
+ )
+
+ def __init__(self, pipeline_bind=None, address=None):
+ super().__init__()
+ self.unk_0 = 0
+ self.unk_8 = 0
+ self.pipeline_bind = pipeline_bind
+ self.unk_10 = 0
+ self.address = address
+ self.unk_18 = 0
+ self.unk_1c_padding = 0
+
+class Start3DArrayAddr(ConstructClass):
+ subcon = Struct(
+ "ptr" / Int64ul,
+ "unk_padding" / Int64ul,
+ )
+
+ def __init__(self, ptr=None):
+ super().__init__()
+ self.ptr = ptr
+ self.unk_padding = 0
+
+class AuxFBInfo(ConstructClass):
+ subcon = Struct(
+ "unk1" / Int32ul,
+ "unk2" / Int32ul,
+ "width" / Dec(Int32ul),
+ "height" / Dec(Int32ul),
+ Ver("V >= V13_0B4", "unk3" / Int64ul),
+ )
+
+ def __init__(self, unk1, unk2, width, height):
+ super().__init__()
+ self.unk1 = unk1
+ self.unk2 = unk2
+ self.width = width
+ self.height = height
+ self.unk3 = 0x100000
+
+class Start3DStruct1(ConstructClass):
+ subcon = Struct(
+ "store_pipeline_bind" / Int32ul, # 0x12, 0x34 seen
+ "store_pipeline_addr" / Int32ul,
+ "unk_8" / Int32ul,
+ "unk_c" / Int32ul,
+ "merge_upper_x" / Float32l,
+ "merge_upper_y" / Float32l,
+ "unk_18" / Int64ul,
+ "tile_blocks_y" / Int16ul, # * 4
+ "tile_blocks_x" / Int16ul, # * 4
+ "unk_24" / Int32ul,
+ "tile_counts" / Int32ul,
+ "unk_2c" / Int32ul,
+ "depth_clear_val1" / Float32l,
+ "stencil_clear_val1" / Int8ul,
+ "unk_35" / Int8ul,
+ "unk_36" / Int16ul,
+ "unk_38" / Int32ul,
+ "unk_3c" / Int32ul,
+ "unk_40" / Int32ul,
+ "unk_44_padding" / HexDump(Bytes(0xac)),
+ "depth_bias_array" / Start3DArrayAddr,
+ "scissor_array" / Start3DArrayAddr,
+ "visibility_result_buffer" / Int64ul,
+ "unk_118" / Int64ul,
+ "unk_120" / Array(37, Int64ul),
+ "unk_reload_pipeline" / Start3DClearPipelineBinding,
+ "unk_258" / Int64ul,
+ "unk_260" / Int64ul,
+ "unk_268" / Int64ul,
+ "unk_270" / Int64ul,
+ "reload_pipeline" / Start3DClearPipelineBinding,
+ "depth_flags" / Int64ul, # 0x40000 - has stencil 0x80000 - has depth
+ "unk_290" / Int64ul,
+ "depth_buffer_ptr1" / Int64ul,
+ "unk_2a0" / Int64ul,
+ "unk_2a8" / Int64ul,
+ "depth_buffer_ptr2" / Int64ul,
+ "depth_buffer_ptr3" / Int64ul,
+ "depth_aux_buffer_ptr" / Int64ul,
+ "stencil_buffer_ptr1" / Int64ul,
+ "unk_2d0" / Int64ul,
+ "unk_2d8" / Int64ul,
+ "stencil_buffer_ptr2" / Int64ul,
+ "stencil_buffer_ptr3" / Int64ul,
+ "stencil_aux_buffer_ptr" / Int64ul,
+ "unk_2f8" / Array(2, Int64ul),
+ "aux_fb_unk0" / Int32ul,
+ "unk_30c" / Int32ul,
+ "aux_fb" / AuxFBInfo,
+ "unk_320_padding" / HexDump(Bytes(0x10)),
+ "unk_partial_store_pipeline" / Start3DStorePipelineBinding,
+ "partial_store_pipeline" / Start3DStorePipelineBinding,
+ "depth_clear_val2" / Float32l,
+ "stencil_clear_val2" / Int8ul,
+ "unk_375" / Int8ul,
+ "unk_376" / Int16ul,
+ "unk_378" / Int32ul,
+ "unk_37c" / Int32ul,
+ "unk_380" / Int64ul,
+ "unk_388" / Int64ul,
+ Ver("V >= V13_0B4", "unk_390_0" / Int64ul),
+ "depth_dimensions" / Int64ul,
+ )
+
+class Start3DStruct2(ConstructClass):
+ subcon = Struct(
+ "unk_0" / Int64ul,
+ "clear_pipeline" / Start3DClearPipelineBinding,
+ "unk_18" / Int64ul,
+ "scissor_array" / Int64ul,
+ "depth_bias_array" / Int64ul,
+ "aux_fb" / AuxFBInfo,
+ "depth_dimensions" / Int64ul,
+ "visibility_result_buffer" / Int64ul,
+ "depth_flags" / Int64ul, # 0x40000 - has stencil 0x80000 - has depth
+ Ver("G >= G14", "unk_58_g14_0" / Int64ul),
+ Ver("G >= G14", "unk_58_g14_8" / Int64ul),
+ "depth_buffer_ptr1" / Int64ul,
+ "depth_buffer_ptr2" / Int64ul,
+ "stencil_buffer_ptr1" / Int64ul,
+ "stencil_buffer_ptr2" / Int64ul,
+ Ver("G >= G14", "unk_68_g14_0" / HexDump(Bytes(0x20))),
+ "unk_78" / Array(4, Int64ul),
+ "depth_aux_buffer_ptr1" / Int64ul,
+ "unk_a0" / Int64ul,
+ "depth_aux_buffer_ptr2" / Int64ul,
+ "unk_b0" / Int64ul,
+ "stencil_aux_buffer_ptr1" / Int64ul,
+ "unk_c0" / Int64ul,
+ "stencil_aux_buffer_ptr2" / Int64ul,
+ "unk_d0" / Int64ul,
+ "tvb_tilemap" / Int64ul,
+ "tvb_heapmeta_addr" / Int64ul,
+ "unk_e8" / Int64ul,
+ "tvb_heapmeta_addr2" / Int64ul,
+ "unk_f8" / Int64ul,
+ "aux_fb_ptr" / Int64ul,
+ "unk_108" / Array(6, Int64ul),
+ "pipeline_base" / Int64ul,
+ "unk_140" / Int64ul,
+ "unk_148" / Int64ul,
+ "unk_150" / Int64ul,
+ "unk_158" / Int64ul,
+ "unk_160" / Int64ul,
+ Ver("G < G14", "unk_168_padding" / HexDump(Bytes(0x1d8))),
+ Ver("G >= G14", "unk_198_padding" / HexDump(Bytes(0x1a8))),
+ Ver("V < V13_0B4", ZPadding(8)),
+ )
+
+class BufferThing(ConstructClass):
+ subcon = Struct(
+ "unk_0" / Int64ul,
+ "unk_8" / Int64ul,
+ "unk_10" / Int64ul,
+ "unkptr_18" / Int64ul,
+ "unk_20" / Int32ul,
+ "bm_misc_addr" / Int64ul,
+ "bm_misc" / ROPointer(this.bm_misc_addr, BufferManagerMisc),
+ "unk_2c" / Int32ul,
+ "unk_30" / Int64ul,
+ "unk_38" / Int64ul,
+ )
+
+class Start3DStruct6(ConstructClass):
+ subcon = Struct(
+ "tvb_overflow_count" / Int64ul,
+ "unk_8" / Int32ul,
+ "unk_c" / Int32ul,
+ "unk_10" / Int32ul,
+ "encoder_id" / Int64ul,
+ "unk_1c" / Int32ul,
+ "unknown_buffer" / Int64ul,
+ "unk_28" / Int64ul,
+ "unk_30" / Int32ul,
+ "unk_34" / Int64ul,
+ )
+
+class Start3DStruct7(ConstructClass):
+ subcon = Struct(
+ "unk_0" / Int64ul,
+ "stamp1_addr" / WrappedPointer, # same contents as below
+ "stamp1" / ROPointer(this.stamp1_addr.value, StampCounter),
+ "stamp2_addr" / WrappedPointer, # same as FinalizeComputeCmd.stamp - some kind of fence/token
+ "stamp2" / ROPointer(this.stamp2_addr.value, StampCounter),
+ "stamp_value" / Int32ul,
+ "ev_3d" / Int32ul,
+ "evctl_index" / Int32ul,
+ "unk_24" / Int32ul,
+ "uuid" / Int32ul,
+ "prev_stamp_value" / Int32ul,
+ "unk_30" / Int32ul,
+ )
+
+ def __init__(self):
+ super().__init__()
+ self.stamp1_addr = StampCounter()
+ self.stamp2_addr = StampCounter()
+
+class Attachment(ConstructClass):
+ subcon = Struct(
+ "address" / Int64ul,
+ "size" / Int32ul,
+ "unk_c" / Int16ul,
+ "unk_e" / Int16ul,
+ )
+
+ def __init__(self, addr, size, unk_c, unk_e):
+ self.address = addr
+ self.size = size
+ self.unk_c = unk_c
+ self.unk_e = unk_e
+
+class Start3DCmd(ConstructClass):
+ subcon = Struct( # 0x194 bytes''''
+ "magic" / Const(0x24, Int32ul),
+ "struct1_addr" / Int64ul, # empty before run. Output? WorkCommand3D + 0x3c0
+ "struct1" / ROPointer(this.struct1_addr, Start3DStruct1),
+ "struct2_addr" / Int64ul, # ?? WorkCommand3D + 0x78
+ "struct2" / ROPointer(this.struct2_addr, Start3DStruct2),
+ "buf_thing_addr" / Int64ul,
+ "buf_thing" / ROPointer(this.buf_thing_addr, BufferThing),
+ "stats_ptr" / Int64ul,
+ "busy_flag_ptr" / Int64ul, # 4 bytes
+ "struct6_addr" / Int64ul, # 0x3c bytes
+ "struct6" / ROPointer(this.struct6_addr, Start3DStruct6),
+ "struct7_addr" / Int64ul, # 0x34 bytes
+ "struct7" / ROPointer(this.struct7_addr, Start3DStruct7),
+ "cmdqueue_ptr" / Int64ul, # points back to the CommandQueueInfo that this command came from
+ "workitem_ptr" / Int64ul, # points back at the WorkItem that this command came from
+ "context_id" / Int32ul,
+ "unk_50" / Int32ul,
+ "event_generation" / Int32ul,
+ "buffer_mgr_slot" / Int32ul,
+ "unk_5c" / Int32ul,
+ "prev_stamp_value" / Int64ul, # 0
+ "unk_68" / Int32ul, # 0
+ "unk_buf_ptr" / Int64ul,
+ "unk_buf2_ptr" / Int64ul, # 0x18 bytes
+ "unk_7c" / Int32ul,
+ "unk_80" / Int32ul,
+ "unk_84" / Int32ul,
+ "uuid" / Int32ul, # uuid for tracking
+ "attachments" / Array(16, Attachment),
+ "num_attachments" / Int32ul,
+ "unk_190" / Int32ul,
+ Ver("V >= V13_0B4", "unk_194" / Int64ul),
+ Ver("V >= V13_0B4", "unkptr_19c" / Int64ul),
+ )
+
+
+class Finalize3DCmd(ConstructClass):
+ subcon = Struct( # 0x9c bytes
+ "magic" / Const(0x25, Int32ul),
+ "uuid" / Int32ul, # uuid for tracking
+ "unk_8" / Int32ul, # 0
+ "stamp_addr" / Int64ul,
+ "stamp" / ROPointer(this.stamp_addr, StampCounter),
+ "stamp_value" / Int32ul,
+ "unk_18" / Int32ul,
+ "buf_thing_addr" / Int64ul,
+ "buf_thing" / ROPointer(this.buf_thing_addr, BufferThing),
+ "buffer_mgr_addr" / Int64ul,
+ "buffer_mgr" / ROPointer(this.buffer_mgr_addr, BufferManagerInfo),
+ "unk_2c" / Int64ul, # 1
+ "stats_ptr" / Int64ul,
+ "struct7_addr" / Int64ul,
+ "struct7" / ROPointer(this.struct7_addr, Start3DStruct7),
+ "busy_flag_ptr" / Int64ul,
+ "cmdqueue_ptr" / Int64ul,
+ "workitem_ptr" / Int64ul,
+ "unk_5c" / Int64ul,
+ "unk_buf_ptr" / Int64ul, # Same as Start3DCmd.unkptr_6c
+ "unk_6c" / Int64ul, # 0
+ "unk_74" / Int64ul, # 0
+ "unk_7c" / Int64ul, # 0
+ "unk_84" / Int64ul, # 0
+ "unk_8c" / Int64ul, # 0
+ Ver("G >= G14", "unk_8c_g14" / Int64ul),
+ "restart_branch_offset" / Int32sl,
+ "unk_98" / Int32ul, # 1
+ Ver("V >= V13_0B4", "unk_9c" / HexDump(Bytes(0x10))),
+ )
+
+class TilingParameters(ConstructClass):
+ subcon = Struct(
+ "size1" / Int32ul,
+ "unk_4" / Int32ul,
+ "unk_8" / Int32ul,
+ "x_max" / Dec(Int16ul),
+ "y_max" / Dec(Int16ul),
+ "tile_count" / Int32ul,
+ "x_blocks" / Int32ul,
+ "y_blocks" / Int32ul,
+ "size2" / Int32ul,
+ "size3" / Int32ul,
+ "unk_24" / Int32ul,
+ "unk_28" / Int32ul,
+ )
+
+class StartTACmdStruct2(ConstructClass):
+ subcon = Struct(
+ "unk_0" / Hex(Int64ul),
+ "unk_8" / Hex(Int32ul),
+ "unk_c" / Hex(Int32ul),
+ "tvb_tilemap" / Hex(Int64ul),
+ Ver("G < G14", "tvb_cluster_tilemaps" / Hex(Int64ul)),
+ "tpc" / Hex(Int64ul),
+ "tvb_heapmeta_addr" / Hex(Int64ul), # like Start3DStruct2.tvb_end_addr with bit 63 set?
+ "iogpu_unk_54" / Int32ul,
+ "iogpu_unk_55" / Int32ul,
+ "iogpu_unk_56" / Int64ul,
+ Ver("G < G14", "tvb_cluster_meta1" / Int64ul),
+ "unk_48" / Int64ul,
+ "unk_50" / Int64ul,
+ "tvb_heapmeta_addr2" / Int64ul,
+ Ver("G < G14", "unk_60" / Int64ul),
+ Ver("G < G14", "core_mask" / Int64ul),
+ "iogpu_deflake_1" / Int64ul,
+ "iogpu_deflake_2" / Int64ul,
+ "unk_80" / Int64ul,
+ "iogpu_deflake_3" / Int64ul, # bit 50 set
+ "encoder_addr" / Int64ul,
+ Ver("G < G14", "tvb_cluster_meta2" / Int64ul),
+ Ver("G < G14", "tvb_cluster_meta3" / Int64ul),
+ Ver("G < G14", "tiling_control" / Int64ul),
+ "unk_b0" / Array(6, Hex(Int64ul)),
+ "pipeline_base" / Int64ul,
+ Ver("G < G14", "tvb_cluster_meta4" / Int64ul),
+ Ver("G < G14", "unk_f0" / Int64ul),
+ "unk_f8" / Int64ul,
+ "unk_100" / Array(3, Hex(Int64ul)),
+ "unk_118" / Int32ul,
+ Ver("G >= G14", Padding(8 * 9)),
+ )
+
+class StartTACmdStruct3(ConstructClass):
+ subcon = Struct(
+ "unk_480" / Array(6, Int32ul),
+ "unk_498" / Int64ul,
+ "unk_4a0" / Int32ul,
+ "iogpu_deflake_1" / Int64ul,
+ "unk_4ac" / Int32ul,
+ "unk_4b0" / Int64ul,
+ "unk_4b8" / Int32ul,
+ "unk_4bc" / Int64ul,
+ "unk_4c4_padding" / HexDump(Bytes(0x48)),
+ "unk_50c" / Int32ul,
+ "unk_510" / Int64ul,
+ "unk_518" / Int64ul,
+ "unk_520" / Int64ul,
+ "unk_528" / Int32ul,
+ "unk_52c" / Int32ul,
+ "unk_530" / Int32ul,
+ "encoder_id" / Int32ul,
+ "unk_538" / Int32ul,
+ "unk_53c" / Int32ul,
+ "unknown_buffer" / Int64ul,
+ "unk_548" / Int64ul,
+ "unk_550" / Array(6, Int32ul),
+ "stamp1_addr" / WrappedPointer, # same contents as below
+ "stamp1" / ROPointer(this.stamp1_addr.value, StampCounter),
+ "stamp2_addr" / WrappedPointer, # same as FinalizeComputeCmd.stamp - some kind of fence/token
+ "stamp2" / ROPointer(this.stamp2_addr.value, StampCounter),
+ "stamp_value" / Int32ul,
+ "ev_ta" / Int32ul,
+ "evctl_index" / Int32ul, # 0-3
+ "unk_584" / Int32ul,
+ "uuid2" / Int32ul,
+ "prev_stamp_value" / Int32ul,
+ "unk_590" / Int32ul,
+ )
+
+ def __init__(self):
+ super().__init__()
+ self.stamp1_addr = StampCounter()
+ self.stamp2_addr = StampCounter()
+
+class StartTACmd(ConstructClass):
+ subcon = Struct(
+ "magic" / Const(0x22, Int32ul),
+ "tiling_params_addr" / Int64ul,
+ "tiling_params" / ROPointer(this.tiling_params_addr, TilingParameters),
+ "struct2_addr" / Int64ul,
+ "struct2" / ROPointer(this.struct2_addr, StartTACmdStruct2),
+ "buffer_mgr_addr" / Int64ul,
+ "buffer_mgr" / ROPointer(this.buffer_mgr_addr, BufferManagerInfo),
+ "buf_thing_addr" / Int64ul,
+ "buf_thing" / ROPointer(this.buf_thing_addr, BufferThing),
+ "stats_ptr" / Int64ul,
+ "cmdqueue_ptr" / Int64ul,
+ "context_id" / Int32ul,
+ "unk_38" / Int32ul,
+ "event_generation" / Int32ul,
+ "buffer_mgr_slot" / Int64ul,
+ "unk_48" / Int64ul,
+ "unk_50" / Int32ul,
+ "struct3_addr" / Int64ul,
+ "struct3" / ROPointer(this.struct3_addr, StartTACmdStruct3),
+ "unkptr_5c" / Int64ul,
+ "unk_5c" / ROPointer(this.unkptr_5c, HexDump(Bytes(0x18))),
+ "unk_64" / Int32ul,
+ "unk_68" / Int32ul,
+ "uuid" / Int32ul,
+ "unk_70" / Int32ul,
+ "unk_74" / Array(29, Int64ul),
+ "unk_15c" / Int32ul,
+ "unk_160" / Int64ul,
+ "unk_168" / Int32ul,
+ "unk_16c" / Int32ul,
+ "unk_170" / Int64ul,
+ "unk_178" / Int32ul,
+ Ver("V >= V13_0B4", "unk_17c" / Int32ul),
+ Ver("V >= V13_0B4", "unkptr_180" / Int64ul),
+ Ver("V >= V13_0B4", "unk_188" / Int32ul),
+ )
+
+class FinalizeTACmd(ConstructClass):
+ subcon = Struct(
+ "magic" / Const(0x23, Int32ul),
+ "buf_thing_addr" / Int64ul,
+ "buf_thing" / ROPointer(this.buf_thing_addr, BufferThing),
+ "buffer_mgr_addr" / Int64ul,
+ "buffer_mgr" / ROPointer(this.buffer_mgr_addr, BufferManagerInfo),
+ "stats_ptr" / Int64ul,
+ "cmdqueue_ptr" / Int64ul, #
+ "context_id" / Int32ul,
+ "unk_28" / Int32ul,
+ "struct3_addr" / Int64ul,
+ "struct3" / ROPointer(this.struct3_addr, StartTACmdStruct3),
+ "unk_34" / Int32ul,
+ "uuid" / Int32ul,
+ "stamp_addr" / Int64ul,
+ "stamp" / ROPointer(this.stamp_addr, StampCounter),
+ "stamp_value" / Int32ul,
+ "unk_48" / Int64ul,
+ "unk_50" / Int32ul,
+ "unk_54" / Int32ul,
+ "unk_58" / Int64ul,
+ "unk_60" / Int32ul,
+ "unk_64" / Int32ul,
+ "unk_68" / Int32ul,
+ Ver("G >= G14", "unk_6c_g14" / Int64ul),
+ "restart_branch_offset" / Int32sl,
+ "unk_70" / Int32ul,
+ Ver("V >= V13_0B4", "unk_74" / HexDump(Bytes(0x10))),
+ )
+
+class ComputeArgs(ConstructClass):
+ subcon = Struct(
+ unk = Bytes(0x7fa0),
+ arg_buffers = Array(8, Int64ul),
+ threadgroups_per_grid_addr = Int64ul,
+ threads_per_threadgroup_addr = Int64ul,
+ )
+
+class ComputeInfo(ConstructClass):
+ # Only the cmdlist and pipelinebase and cmdlist fields are strictly needed to launch a basic
+ # compute shader.
+ subcon = Struct( # 0x1c bytes
+ "args" / Int64ul, # ComputeArgs
+ "cmdlist" / Int64ul, # CommandList from userspace
+ "unkptr_10" / Int64ul, # size 8, null
+ "unkptr_18" / Int64ul, # size 8, null
+ "unkptr_20" / Int64ul, # size 8, null
+ "unkptr_28" / Int64ul, #
+ "pipeline_base" / Int64ul, # 0x11_00000000: Used for certain "short" pointers like pipelines (and shaders?)
+ "unk_38" / Int64ul, # always 0x8c60.
+ "unk_40" / Int32ul, # 0x41
+ "unk_44" / Int32ul, # 0
+ "unkptr_48" / Int64ul, # related to threadgroups / thread layout
+ "unk_50" / Int32ul, # 0x40 - Size?
+ "unk_54" / Int32ul, # 0
+ "unk_58" / Int32ul, # 1
+ "unk_5c" / Int32ul, # 0
+ "unk_60" / Int32ul, # 0x1c
+ )
+
+# Related to "IOGPU Misc"
+class ComputeInfo2(ConstructClass):
+ subcon = Struct(
+ unk_0 = HexDump(Bytes(0x24)),
+ unkptr_24 = Int64ul, # equal to args
+ unkptr_2c = Int64ul, # points at end of cmdlist?
+ unk_34 = HexDump(Bytes(0x38)),
+ encoder_id = Int32ul,
+ unk_70 = Int32ul,
+ unk_74 = Int32ul,
+ unknown_buffer = Int64ul,
+ unk_80 = Int32ul,
+ unk_84 = Int32ul,
+ unk_88 = Int32ul,
+ stamp1_addr = Int64ul, # same contents as below
+ stamp1 = ROPointer(this.stamp1_addr, Hex(Int32ul)),
+ stamp2_addr = Int64ul, # same as FinalizeComputeCmd.stamp - some kind of fence/token
+ stamp2 = ROPointer(this.stamp2_addr, Hex(Int32ul)),
+ stamp_value = Int32ul,
+ unk_a0 = Int32ul,
+ unk_a4 = Int32ul,
+ unk_a8 = Int32ul,
+ uuid = Int32ul,
+ unk_b0 = Int32ul,
+ )
+
+class StartComputeCmd(ConstructClass):
+ subcon = Struct( # 0x154 bytes''''
+ "magic" / Const(0x29, Int32ul),
+ "unkptr_4" / Int64ul, # empty: WorkCommandCP + 0x14, size: 0x54
+ "computeinfo_addr" / Int64ul, # List of userspace VAs: WorkCommandCP + 0x68
+ "computeinfo" / ROPointer(this.computeinfo_addr, ComputeInfo),
+ "unkptr_14" / Int64ul, # In gpu-asc's heap? Did this pointer come from the gfx firmware?
+ "cmdqueue_ptr" / Int64ul, # points back to the submitinfo that this command came from
+ "context_id" / Int32ul, # 4
+ "unk_28" / Int32ul, # 1
+ "unk_2c" / Int32ul, # 0
+ "unk_30" / Int32ul,
+ "unk_34" / Int32ul,
+ "unk_38" / Int32ul,
+ "computeinfo2_addr" / Int64ul, # WorkCommandCP + 0x1f4
+ "computeinfo2" / ROPointer(this.computeinfo2_addr, ComputeInfo2),
+ "unk_44" / Int32ul,
+ "uuid" / Int32ul, # uuid for tracking?
+ "padding" / Bytes(0x154 - 0x4c),
+ )
+
+class FinalizeComputeCmd(ConstructClass):
+ subcon = Struct( # 0x64 bytes''''
+ "magic" / Const(0x2a, Int32ul),
+ "unkptr_4" / Int64ul, # same as ComputeStartCmd.unkptr_14
+ "cmdqueue_ptr" / Int64ul, # points back to the submitinfo
+ "unk_14" / Int32ul, # Context ID?
+ "unk_18" / Int32ul,
+ "unkptr_1c" / Int64ul, # same as ComputeStartCmd.unkptr_3c
+ "unk_24" / Int32ul,
+ "uuid" / Int32ul, # uuid for tracking?
+ "stamp" / Int64ul,
+ "stamp_value" / Int32ul, # Gets written to unkptr_2c (after stamp?)
+ "unk_38" / Int32ul,
+ "unk_3c" / Int32ul,
+ "unk_40" / Int32ul,
+ "unk_44" / Int32ul,
+ "unk_48" / Int32ul,
+ "unk_4c" / Int32ul,
+ "unk_50" / Int32ul,
+ "unk_54" / Int32ul,
+ "unk_58" / Int32ul,
+ Ver("G >= G14", "unk_5c_g14" / Int64ul),
+ "restart_branch_offset" / Int32sl, # realative offset from start of Finalize to StartComputeCmd
+ "unk_60" / Int32ul,
+ )
+
+class EndCmd(ConstructClass):
+ subcon = Struct(
+ "magic" / Const(0x18, Int8ul),
+ "unk_1" / Int8ul,
+ "unk_2" / Int8ul,
+ "flags" / Int8ul,
+ )
+
+ def __init__(self):
+ super().__init__()
+ self.unk_1 = 0
+ self.unk_2 = 0
+ self.flags = 0x40
+
+class TimestampCmd(ConstructClass):
+ subcon = Struct( # 0x34 bytes
+ "magic" / Const(0x19, Int8ul),
+ "unk_1" / Int8ul,
+ "unk_2" / Int8ul,
+ "unk_3" / Int8ul, # Sometimes 0x80
+ # all these pointers point to 0xfa0... addresses. Might be where the timestamp should be writen?
+ "ts0_addr" / Int64ul,
+ "ts0" / ROPointer(this.ts0_addr, TimeStamp),
+ "ts1_addr" / Int64ul,
+ "ts1" / ROPointer(this.ts1_addr, TimeStamp),
+ "ts2_addr" / Int64ul,
+ "ts2" / ROPointer(this.ts2_addr, TimeStamp),
+ "cmdqueue_ptr" / Int64ul,
+ "unk_24" / Int64ul,
+ Ver("V >= V13_0B4", "unkptr_2c_0" / Int64ul),
+ "uuid" / Int32ul,
+ "unk_30_padding" / Int32ul,
+ )
+
+class WaitForInterruptCmd(ConstructClass):
+ subcon = Struct(
+ "magic" / Const(0x01, Int8ul),
+ "unk_1" / Int8ul,
+ "unk_2" / Int8ul,
+ "unk_3" / Int8ul,
+ )
+
+ def __init__(self, unk_1, unk_2, unk_3):
+ super().__init__()
+ self.unk_1 = unk_1
+ self.unk_2 = unk_2
+ self.unk_3 = unk_3
+
+class NopCmd(ConstructClass):
+ # This doesn't exist
+ subcon = Struct(
+ "magic" / Const(0x00, Int32ul),
+ )
+
+ def __str__(self) -> str:
+ return "Nop"
+
+
+class MicroSequence(ConstructValueClass):
+ subcon = RepeatUntil(lambda obj, lst, ctx: lst[-1].cmdid == 0x18,
+ Struct(
+ "cmdid" / Peek(Int8ul),
+ "cmd" / Switch(this.cmdid, {
+ 0x01: WaitForInterruptCmd,
+ 0x18: EndCmd,
+ 0x19: TimestampCmd,
+ 0x22: StartTACmd,
+ 0x23: FinalizeTACmd,
+ 0x24: Start3DCmd,
+ 0x25: Finalize3DCmd,
+ 0x29: StartComputeCmd,
+ 0x2a: FinalizeComputeCmd,
+ }, default=Error)
+ )
+ )
+
+ def __str__(self):
+ s = "{\n"
+ for cmd in self.value:
+ s += str(cmd.cmd) + '\n'
+ if isinstance(cmd.cmd, EndCmd):
+ s += "}\n"
+ break
+ else:
+ s += "?\n"
+ return s
+
+__all__.extend(k for k, v in globals().items()
+ if (callable(v) or isinstance(v, type)) and v.__module__ == __name__)
diff --git a/tools/proxyclient/m1n1/fw/aop/__init__.py b/tools/proxyclient/m1n1/fw/aop/__init__.py
new file mode 100644
index 0000000..6828232
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/aop/__init__.py
@@ -0,0 +1,31 @@
+# SPDX-License-Identifier: MIT
+from .bootargs import ASCArgumentSection
+
+class AOPBase:
+ def __init__(self, u, adtnode):
+ self.fw_base, self.fw_len = adtnode.get_reg(2)
+ if u.adt["arm-io"].compatible[0] == "arm-io,t6000":
+ # argh
+ self.fw_base -= 0x2_0000_0000
+
+ @property
+ def _bootargs_span(self):
+ base = self.fw_base + self.u.proxy.read32(self.fw_base + 0x224)
+ length = self.u.proxy.read32(self.fw_base + 0x228)
+
+ return (base, length)
+
+ def read_bootargs(self):
+ blob = self.u.proxy.iface.readmem(*self._bootargs_span)
+ return ASCArgumentSection(blob)
+
+ def write_bootargs(self, args):
+ base, _ = self._bootargs_span
+ self.u.proxy.iface.writemem(base, args.to_bytes())
+
+ def update_bootargs(self, keyvals):
+ args = self.read_bootargs()
+ args.update(keyvals)
+ self.write_bootargs(args)
+
+__all__ = ["ASCArgumentSection", "AOPBase"]
diff --git a/tools/proxyclient/m1n1/fw/aop/bootargs.py b/tools/proxyclient/m1n1/fw/aop/bootargs.py
new file mode 100644
index 0000000..100e4f3
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/aop/bootargs.py
@@ -0,0 +1,64 @@
+# SPDX-License-Identifier: MIT
+
+class ASCArgumentSection:
+ def __init__(self, bytes_):
+ self.blob = bytearray(bytes_)
+ self.index = self.build_index()
+
+ def build_index(self):
+ off = 0
+ fields = []
+ while off < len(self.blob):
+ snip = self.blob[off:]
+ key = snip[0:4]
+ length = int.from_bytes(snip[4:8], byteorder='little')
+ fields.append((key.decode('ascii'), (off + 8, length)))
+ off += 8 + length
+
+ if off > len(self.blob):
+ raise ValueError('blob overran during parsing')
+
+ return dict(fields)
+
+ def items(self):
+ for key, span in self.index.items():
+ off, length = span
+ yield key, self.blob[off:off + length]
+
+ def __getitem__(self, key):
+ off, length = self.index[key]
+ return bytes(self.blob[off:off + length])
+
+ def __setitem__(self, key, value):
+ off, length = self.index[key]
+
+ if type(value) is int:
+ value = int.to_bytes(value, length, byteorder='little')
+ elif type(value) is str:
+ value = value.encode('ascii')
+
+ if len(value) > length:
+ raise ValueError(f'field {key:s} overflown')
+
+ self.blob[off:off + length] = value
+
+ def update(self, keyvals):
+ for key, val in keyvals.items():
+ self[key] = val
+
+ def keys(self):
+ return self.index.keys()
+
+ def dump(self):
+ for key, val in self.items():
+ print(f"{key:4s} = {val}")
+
+ def dump_diff(self, other, logger):
+ assert self.index == other.index
+
+ for key in self.keys():
+ if self[key] != other[key]:
+ logger(f"\t{key:4s} = {self[key]} -> {other[key]}")
+
+ def to_bytes(self):
+ return bytes(self.blob)
diff --git a/tools/proxyclient/m1n1/fw/aop/ipc.py b/tools/proxyclient/m1n1/fw/aop/ipc.py
new file mode 100644
index 0000000..d26315d
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/aop/ipc.py
@@ -0,0 +1,365 @@
+from enum import IntEnum
+from construct import *
+from io import BytesIO
+
+from m1n1.utils import FourCC, chexdump
+from m1n1.constructutils import ZPadding
+from m1n1.fw.afk.epic import EPICCmd, EPICCategory
+
+
+EPICSubHeaderVer2 = Struct(
+ "length" / Int32ul,
+ "version" / Default(Int8ul, 2),
+ "category" / EPICCategory,
+ "type" / Hex(Int16ul),
+ "timestamp" / Default(Int64ul, 0),
+ "unk1" / Default(Hex(Int32ul), 0),
+ "unk2" / Default(Hex(Int32ul), 0),
+)
+
+class AOPAudioPropKey(IntEnum):
+ IS_READY = 0x01
+
+ UNK_11 = 0x11
+ PLACEMENT = 0x1e
+ UNK_21 = 0x21
+ ORIENTATION = 0x2e
+ LOCATION_ID = 0x30
+ SERIAL_NO = 0x3e
+ VENDOR_ID = 0x5a
+ PRODUCT_ID = 0x5b
+
+ SERVICE_CONTROLLER = 0x64
+ DEVICE_COUNT = 0x65
+
+ VERSION = 0x67
+
+class EPICCall:
+ @classmethod
+ def matches(cls, hdr, sub):
+ return int(sub.type) == cls.TYPE
+
+ def _args_fixup(self):
+ pass
+
+ def __init__(self, *args, **kwargs):
+ if args:
+ self.args = args[0]
+ else:
+ self.args = Container(**kwargs)
+ self._args_fixup()
+ self.rets = None
+
+ @classmethod
+ def from_stream(cls, f):
+ return cls(cls.ARGS.parse_stream(f))
+
+ def dump(self, logger=None):
+ if logger is None:
+ logger = print
+ args_fmt = [f"{k}={v}" for (k, v) in self.args.items() if k != "_io"]
+ rets_fmt = [f"{k}={v}" for (k, v) in self.rets.items() if k != "_io"]
+ logger(f"{type(self).__name__}({', '.join(args_fmt)}) -> ({', '.join(rets_fmt)})")
+
+ def read_resp(self, f):
+ self.rets = self.RETS.parse_stream(f)
+
+CALLTYPES = []
+def reg_calltype(calltype):
+ CALLTYPES.append(calltype)
+ return calltype
+
+@reg_calltype
+class GetHIDDescriptor(EPICCall):
+ TYPE = 0x1
+ ARGS = Struct(
+ "blank" / Const(0x0, Int32ul),
+ )
+ RETS = Struct(
+ "retcode" / Default(Hex(Int32ul), 0),
+ "descriptor" / HexDump(GreedyBytes),
+ )
+
+@reg_calltype
+class GetProperty(EPICCall):
+ TYPE = 0xa
+ ARGS = Struct(
+ "blank" / Const(0x0, Int32ul),
+ "key" / Enum(Int32ul, AOPAudioPropKey),
+ )
+ RETS = Struct(
+ #"blank" / Const(0x0, Int32ul),
+ "value" / GreedyBytes,
+ )
+
+@reg_calltype
+class WrappedCall(EPICCall):
+ SUBCLASSES = {}
+ TYPE = 0x20
+ HDR = Struct(
+ "blank" / Const(0x0, Int32ul),
+ "unk1" / Hex(Const(0xffffffff, Int32ul)),
+ "calltype" / Hex(Int32ul),
+ "blank2" / ZPadding(16),
+ "pad" / Hex(Int32ul),
+ "len" / Hex(Int64ul),
+ "residue" / HexDump(GreedyBytes),
+ )
+
+ @classmethod
+ def from_stream(cls, f):
+ payload = f.read()
+ subsub = cls.HDR.parse(payload)
+ calltype = int(subsub.calltype)
+ subcls = cls.SUBCLASSES.get(calltype, None)
+ if subcls is None:
+ raise ValueError(f"unknown calltype {calltype:#x}")
+ return subcls(subcls.ARGS.parse(payload))
+
+ @classmethod
+ def reg_subclass(cls, cls2):
+ cls.SUBCLASSES[int(cls2.CALLTYPE)] = cls2
+ return cls2
+
+ @classmethod
+ def matches(cls, hdr, sub):
+ return sub.category == EPICCategory.NOTIFY and sub.type == cls.TYPE
+
+ def check_retcode(self):
+ if self.rets.retcode:
+ self.dump()
+ raise ValueError(f"retcode {self.rets.retcode} in {str(type(self))} (call dumped, see above)")
+
+@WrappedCall.reg_subclass
+class AttachDevice(WrappedCall):
+ CALLTYPE = 0xc3_00_00_02
+ ARGS = Struct(
+ "blank" / Const(0x0, Int32ul),
+ "unk1" / Hex(Const(0xffffffff, Int32ul)),
+ "calltype" / Hex(Const(0xc3000002, Int32ul)),
+ "blank2" / ZPadding(16),
+ "pad" / Padding(4),
+ "len" / Hex(Const(0x2c, Int64ul)),
+ "devid" / FourCC,
+ "pad" / Padding(4),
+ )
+ RETS = Struct(
+ "retcode" / Default(Hex(Int32ul), 0),
+ "unk" / HexDump(GreedyBytes),
+ )
+
+@WrappedCall.reg_subclass
+class ProbeDevice(WrappedCall):
+ CALLTYPE = 0xc3_00_00_01
+ ARGS = Struct(
+ "blank" / Const(0x0, Int32ul),
+ "unk1" / Hex(Const(0xffffffff, Int32ul)),
+ "calltype" / Hex(Const(0xc3000001, Int32ul)),
+ "blank2" / ZPadding(16),
+ "pad" / Padding(4),
+ "len" / Hex(Const(0x28, Int64ul)),
+ "devno" / Int32ul,
+ )
+ RETS = Struct(
+ "retcode" / Default(Hex(Int32ul), 0),
+ "devid" / FourCC,
+ "blank2" / Const(0x0, Int32ul),
+ "unk1" / Const(8, Int32ul),
+ "blank3" / Const(0x0, Int32ul),
+ "unk2" / Hex(Const(0x01_0d_1c_20, Int32ul)),
+ "blank4" / Const(0x0, Int32ul),
+ "remainder" / HexDump(GreedyBytes),
+ )
+
+PDMConfig = Struct(
+ "unk1" / Int32ul,
+ "clockSource" / FourCC,
+ "pdmFrequency" / Int32ul,
+ "unk3_clk" / Int32ul,
+ "unk4_clk" / Int32ul,
+ "unk5_clk" / Int32ul,
+ "channelPolaritySelect" / Hex(Int32ul),
+ "unk7" / Hex(Int32ul),
+ "unk8" / Hex(Int32ul),
+ "unk9" / Hex(Int16ul),
+ "ratios" / Struct(
+ "r1" / Int8ul,
+ "r2" / Int8ul,
+ "r3" / Int8ul,
+ "pad" / Default(Int8ul, 0),
+ ),
+ "filterLengths" / Hex(Int32ul),
+ "coeff_bulk" / Int32ul,
+ #"coefficients" / Struct(
+ # "c1" / Int32sl[this._.ratios.r3 * 4 + 4],
+ # "c2" / Int32sl[this._.ratios.r2 * 4 + 4],
+ # "c3" / Int32sl[this._.ratios.r1 * 4 + 4],
+ #),
+ #"junk" / Padding(
+ # this.coeff_bulk * 4 - 48 \
+ # - (this.ratios.r1 + this.ratios.r2 + this.ratios.r3) * 16
+ #),
+ "coefficients" / Int32sl[
+ (this.ratios.r1 + this.ratios.r2 + this.ratios.r3) * 4 + 12
+ ],
+ "junk" / Padding(
+ lambda this: max(0,
+ this.coeff_bulk * 4 - 48 \
+ - (this.ratios.r1 + this.ratios.r2 + this.ratios.r3) * 16
+ )
+ ),
+ "unk10" / Int32ul, # maybe
+ "micTurnOnTimeMs" / Int32ul,
+ "blank" / ZPadding(16),
+ "unk11" / Int32ul,
+ "micSettleTimeMs" / Int32ul,
+ "blank2" / ZPadding(69),
+)
+
+DecimatorConfig = Struct(
+ "latency" / Int32ul,
+ "ratios" / Struct(
+ "r1" / Int8ul,
+ "r2" / Int8ul,
+ "r3" / Int8ul,
+ "pad" / Default(Int8ul, 0),
+ ),
+ "filterLengths" / Hex(Int32ul),
+ "coeff_bulk" / Int32ul,
+ "coefficients" / Int32sl[
+ (this.ratios.r1 + this.ratios.r2 + this.ratios.r3) * 4 + 12
+ ],
+ "junk" / Padding(
+ lambda this: max(0,
+ this.coeff_bulk * 4 - 48 \
+ - (this.ratios.r1 + this.ratios.r2 + this.ratios.r3) * 16
+ )
+ ),
+)
+
+PowerSetting = Struct(
+ "devid" / FourCC,
+ "cookie" / Int32ul,
+ "pad" / Padding(4),
+ "blank" / ZPadding(8),
+ "target_pstate" / FourCC,
+ "unk2" / Int32ul,
+ "blank2" / ZPadding(20),
+)
+
+DEVPROPS = {
+ ('hpai', 202): PowerSetting,
+ ('lpai', 202): PowerSetting,
+ ('hpai', 200): FourCC,
+ ('lpai', 200): FourCC,
+ ('pdm0', 200): PDMConfig,
+ ('pdm0', 210): DecimatorConfig,
+ ('lpai', 301): Struct(
+ "unk1" / Int32ul,
+ "unk2" / Int32ul,
+ "unk3" / Int32ul,
+ "unk4" / Int32ul,
+ ),
+}
+
+@WrappedCall.reg_subclass
+class GetDeviceProp(WrappedCall):
+ CALLTYPE = 0xc3_00_00_04
+ ARGS = Struct(
+ "blank" / Const(0x0, Int32ul),
+ "unk1" / Hex(Const(0xffffffff, Int32ul)),
+ "calltype" / Hex(Const(0xc3000004, Int32ul)),
+ "blank2" / ZPadding(16),
+ "pad" / Padding(4),
+ "len" / Hex(Const(0x30, Int64ul)),
+ "devid" / FourCC,
+ "modifier" / Int32ul,
+ "unk6" / Hex(Const(0x01, Int32ul)),
+ )
+ RETS = Struct(
+ "retcode" / Default(Hex(Int32ul), 0),
+ "len" / Optional(Int32ul),
+ "data" / Switch(lambda s: (s._params.devid, s._params.modifier),
+ DEVPROPS,
+ default=HexDump(GreedyBytes))
+ )
+
+ def read_resp(self, f):
+ self.rets = self.RETS.parse_stream(f,
+ devid=self.args.devid, modifier=self.args.modifier
+ )
+
+@WrappedCall.reg_subclass
+class SetDeviceProp(WrappedCall):
+ CALLTYPE = 0xc3_00_00_05
+ ARGS = Struct(
+ "blank" / Const(0x0, Int32ul),
+ "unk1" / Hex(Const(0xffffffff, Int32ul)),
+ "calltype" / Hex(Const(0xc3000005, Int32ul)),
+ "blank2" / ZPadding(16),
+ "pad" / Padding(4),
+ "len" / Hex(Int64ul), # len(this.data) + 0x30
+ "devid" / FourCC,
+ "modifier" / Int32ul,
+ "len2" / Hex(Int32ul), # len(this.data)
+ "data" / Switch(lambda s: (s.devid, s.modifier),
+ DEVPROPS,
+ default=HexDump(GreedyBytes))
+ )
+ RETS = Struct(
+ "retcode" / Default(Hex(Int32ul), 0),
+ "unk" / HexDump(GreedyBytes),
+ )
+
+ def _args_fixup(self):
+ data_len = len(self.ARGS.build(Container(len=0, len2=0, **self.args))) - 52
+ if 'len' not in self.args:
+ self.args.len = data_len + 0x30
+ if 'len2' not in self.args:
+ self.args.len2 = data_len
+
+@reg_calltype
+class IndirectCall(EPICCall):
+ ARGS = EPICCmd
+ RETS = EPICCmd
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.txbuf = None
+ self.rxbuf = None
+
+ @classmethod
+ def matches(cls, hdr, sub):
+ return sub.category == EPICCategory.COMMAND
+
+ def read_txbuf(self, ep):
+ cmd = self.args
+ ep.dart.invalidate_cache()
+ self.txbuf = ep.dart.ioread(0, cmd.txbuf, cmd.txlen)
+
+ # dump the command data for offline replays of traces
+ ep.log(f"===COMMAND TX DATA=== addr={cmd.txbuf:#x}")
+ chexdump(self.txbuf)
+ ep.log(f"===END DATA===")
+
+ def read_rxbuf(self, ep):
+ cmd = self.rets
+ ep.dart.invalidate_cache()
+ self.rxbuf = ep.dart.ioread(0, cmd.rxbuf, cmd.rxlen)
+
+ ep.log(f"===COMMAND RX DATA=== addr={cmd.rxbuf:#x}")
+ chexdump(self.rxbuf)
+ ep.log(f"===END DATA===")
+
+ def unwrap(self):
+ fd = BytesIO()
+ fd.write(b"\x00\x00\x00\x00")
+ fd.write(self.txbuf)
+ fd.seek(0)
+ wrapped = WrappedCall.from_stream(fd)
+ fd = BytesIO()
+ fd.write(b"\x00\x00\x00\x00")
+ fd.write(self.rxbuf)
+ fd.seek(0)
+ wrapped.read_resp(fd)
+ return wrapped
diff --git a/tools/proxyclient/m1n1/fw/asc/__init__.py b/tools/proxyclient/m1n1/fw/asc/__init__.py
new file mode 100644
index 0000000..4ffdb8b
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/asc/__init__.py
@@ -0,0 +1,125 @@
+# SPDX-License-Identifier: MIT
+from ...utils import *
+
+from .crash import ASCCrashLogEndpoint
+from .syslog import ASCSysLogEndpoint
+from .mgmt import ASCManagementEndpoint
+from .kdebug import ASCKDebugEndpoint
+from .ioreporting import ASCIOReportingEndpoint
+from .oslog import ASCOSLogEndpoint
+from .base import ASCBaseEndpoint, ASCTimeout
+from ...hw.asc import ASC
+
+__all__ = []
+
+class ASCDummyEndpoint(ASCBaseEndpoint):
+ SHORT = "dummy"
+
+class StandardASC(ASC):
+ ENDPOINTS = {
+ 0: ASCManagementEndpoint,
+ 1: ASCCrashLogEndpoint,
+ 2: ASCSysLogEndpoint,
+ 3: ASCKDebugEndpoint,
+ 4: ASCIOReportingEndpoint,
+ 8: ASCOSLogEndpoint,
+ 0xa: ASCDummyEndpoint, # tracekit
+ }
+
+ def __init__(self, u, asc_base, dart=None, stream=0):
+ super().__init__(u, asc_base)
+ self.remote_eps = set()
+ self.add_ep(0, ASCManagementEndpoint(self, 0))
+ self.dart = dart
+ self.stream = stream
+ self.eps = []
+ self.epcls = {}
+ self.dva_offset = 0
+ self.dva_size = 1 << 32
+ self.allow_phys = False
+
+ for cls in type(self).mro():
+ eps = getattr(cls, "ENDPOINTS", None)
+ if eps is None:
+ break
+ for k, v in eps.items():
+ if k not in self.epcls:
+ self.epcls[k] = v
+
+ def addr(self, addr):
+ return f"{addr:#x}"
+
+ def iomap(self, addr, size):
+ if self.dart is None:
+ return addr
+ dva = self.dva_offset | self.dart.iomap(self.stream, addr, size)
+
+ self.dart.invalidate_streams(1)
+ return dva
+
+ def ioalloc(self, size):
+ paddr = self.u.memalign(0x4000, size)
+ dva = self.iomap(paddr, size)
+ return paddr, dva
+
+ def ioread(self, dva, size):
+ if self.allow_phys and dva < self.dva_offset or dva >= (self.dva_offset + self.dva_size):
+ return self.iface.readmem(dva, size)
+
+ if self.dart:
+ return self.dart.ioread(self.stream, dva & 0xFFFFFFFFF, size)
+ else:
+ return self.iface.readmem(dva, size)
+
+ def iowrite(self, dva, data):
+ if self.allow_phys and dva < self.dva_offset or dva >= (self.dva_offset + self.dva_size):
+ return self.iface.writemem(dva, data)
+
+ if self.dart:
+ return self.dart.iowrite(self.stream, dva & 0xFFFFFFFFF, data)
+ else:
+ return self.iface.writemem(dva, data)
+
+ def iotranslate(self, dva, size):
+ if self.allow_phys and dva < self.dva_offset or dva >= (self.dva_offset + self.dva_size):
+ return [(dva, size)]
+
+ if self.dart:
+ return self.dart.iotranslate(self.stream, dva & 0xFFFFFFFFF, size)
+ else:
+ return [(dva, size)]
+
+ def start_ep(self, epno):
+ if epno not in self.epcls:
+ raise Exception(f"Unknown endpoint {epno:#x}")
+
+ epcls = self.epcls[epno]
+ ep = epcls(self, epno)
+ self.add_ep(epno, ep)
+ print(f"Starting endpoint #{epno:#x} ({ep.name})")
+ self.mgmt.start_ep(epno)
+ ep.start()
+
+ def start(self):
+ super().boot()
+ self.mgmt.start()
+ self.mgmt.wait_boot(3)
+
+ def stop(self, state=0x10):
+ for ep in list(self.epmap.values())[::-1]:
+ if ep.epnum < 0x10:
+ continue
+ ep.stop()
+ self.mgmt.stop(state=state)
+ self.epmap = {}
+ self.add_ep(0, ASCManagementEndpoint(self, 0))
+ if state < 0x10:
+ self.shutdown()
+
+ def boot(self):
+ print("Booting ASC...")
+ super().boot()
+ self.mgmt.wait_boot(1)
+
+__all__.extend(k for k, v in globals().items()
+ if (callable(v) or isinstance(v, type)) and v.__module__ == __name__)
diff --git a/tools/proxyclient/m1n1/fw/asc/base.py b/tools/proxyclient/m1n1/fw/asc/base.py
new file mode 100644
index 0000000..b10aaaf
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/asc/base.py
@@ -0,0 +1,59 @@
+# SPDX-License-Identifier: MIT
+from ...utils import *
+
+# System endpoints
+def msg_handler(message, regtype=None):
+ def f(x):
+ x.is_message = True
+ x.message = message
+ x.regtype = regtype
+ return x
+
+ return f
+
+class ASCMessage1(Register64):
+ EP = 7, 0
+
+class ASCTimeout(Exception):
+ pass
+
+class ASCBaseEndpoint:
+ BASE_MESSAGE = Register64
+ SHORT = None
+
+ def __init__(self, asc, epnum, name=None):
+ self.asc = asc
+ self.epnum = epnum
+ self.name = name or self.SHORT or f"{type(self).__name__}@{epnum:#x}"
+
+ self.msghandler = {}
+ self.msgtypes = {}
+ for name in dir(self):
+ i = getattr(self, name)
+ if not callable(i):
+ continue
+ if not getattr(i, "is_message", False):
+ continue
+ self.msghandler[i.message] = i
+ self.msgtypes[i.message] = i.regtype if i.regtype else self.BASE_MESSAGE
+
+ def handle_msg(self, msg0, msg1):
+ msg0 = self.BASE_MESSAGE(msg0)
+ handler = self.msghandler.get(msg0.TYPE, None)
+ regtype = self.msgtypes.get(msg0.TYPE, self.BASE_MESSAGE)
+
+ if handler is None:
+ return False
+ return handler(regtype(msg0.value))
+
+ def send(self, msg):
+ self.asc.send(msg, ASCMessage1(EP=self.epnum))
+
+ def start(self):
+ pass
+
+ def stop(self):
+ pass
+
+ def log(self, msg):
+ print(f"[{self.name}] {msg}")
diff --git a/tools/proxyclient/m1n1/fw/asc/crash.py b/tools/proxyclient/m1n1/fw/asc/crash.py
new file mode 100644
index 0000000..7f590c8
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/asc/crash.py
@@ -0,0 +1,248 @@
+# SPDX-License-Identifier: MIT
+from .base import *
+from ...utils import *
+from construct import *
+from ...sysreg import *
+
+class CrashLogMessage(Register64):
+ TYPE = 63, 52
+ SIZE = 51, 44
+ DVA = 43, 0
+
+CrashHeader = Struct(
+ "type" / Const("CLHE", FourCC),
+ "ver" / Int32ul,
+ "total_size" / Int32ul,
+ "flags" / Int32ul,
+ Padding(16)
+)
+
+CrashCver = Struct(
+ "uuid" / Bytes(16),
+ "version" / CString("utf8"),
+)
+
+CrashCstr = Struct(
+ "id" / Int32ul,
+ "string" / CString("utf8"),
+)
+
+CrashCtim = Struct(
+ "time" / Int64ul,
+)
+
+CrashCmbx = Struct(
+ "hdr" / Array(4, Hex(Int32ul)),
+ "type" / Int32ul,
+ "unk" / Int32ul,
+ "index" / Int32ul,
+ "messages" / GreedyRange(Struct(
+ "endpoint" / Hex(Int64ul),
+ "message" / Hex(Int64ul),
+ "timestamp" / Hex(Int32ul),
+ Padding(4),
+ )),
+)
+
+CrashCcst = Struct(
+ "task" / Int32ul,
+ "unk" / Int32ul,
+ "stack" / GreedyRange(Int64ul)
+)
+
+CrashCasC = Struct(
+ "l2c_err_sts" / Hex(Int64ul),
+ "l2c_err_adr" / Hex(Int64ul),
+ "l2c_err_inf" / Hex(Int64ul),
+ "lsu_err_sts" / Hex(Int64ul),
+ "fed_err_sts" / Hex(Int64ul),
+ "mmu_err_sts" / Hex(Int64ul)
+)
+
+CrashCrg8 = Struct(
+ "unk_0" / Int32ul,
+ "unk_4" / Int32ul,
+ "regs" / Array(31, Hex(Int64ul)),
+ "sp" / Int64ul,
+ "pc" / Int64ul,
+ "psr" / Int64ul,
+ "cpacr" / Int64ul,
+ "fpsr" / Int64ul,
+ "fpcr" / Int64ul,
+ "unk" / Array(64, Hex(Int64ul)),
+ "far" / Int64ul,
+ "unk_X" / Int64ul,
+ "esr" / Int64ul,
+ "unk_Z" / Int64ul,
+)
+
+CrashEntry = Struct(
+ "type" / FourCC,
+ Padding(4),
+ "flags" / Hex(Int32ul),
+ "len" / Int32ul,
+ "payload" / FixedSized(lambda ctx: ctx.len - 16 if ctx.type != "CLHE" else 16,
+ Switch(this.type, {
+ "Cver": CrashCver,
+ "Ctim": CrashCtim,
+ "Cmbx": CrashCmbx,
+ "Cstr": CrashCstr,
+ "Crg8": CrashCrg8,
+ "Ccst": CrashCcst,
+ "CasC": CrashCasC,
+ }, default=GreedyBytes)),
+)
+
+CrashLog = Struct(
+ "header" / CrashHeader,
+ "entries" / RepeatUntil(this.type == "CLHE", CrashEntry),
+)
+
+class CrashLogParser:
+ def __init__(self, data=None, asc=None):
+ self.asc = asc
+ if data is not None:
+ self.parse(data)
+
+ def parse(self, data):
+ self.data = CrashLog.parse(data)
+ pass
+
+ def default(self, entry):
+ print(f"# {entry.type} flags={entry.flags:#x}")
+ chexdump(entry.payload)
+ print()
+
+ def Ccst(self, entry):
+ print(f"Call stack (task {entry.payload.task}:")
+ for i in entry.payload.stack:
+ if not i:
+ break
+ print(f" - {i:#x}")
+ print()
+
+ def CasC(self, entry):
+ print(f"Async error info:")
+ print(entry.payload)
+ print()
+
+ def Cver(self, entry):
+ print(f"RTKit Version: {entry.payload.version}")
+ print()
+
+ def Crg8(self, entry):
+ print(f"Exception info:")
+
+ ctx = entry.payload
+
+ addr = self.asc.addr
+
+ spsr = SPSR(ctx.psr)
+ esr = ESR(ctx.esr)
+ elr = ctx.pc
+ far_phys = self.asc.iotranslate(ctx.far, 1)[0][0]
+ elr_phys = self.asc.iotranslate(ctx.pc, 1)[0][0]
+ sp_phys = self.asc.iotranslate(ctx.sp, 1)[0][0]
+
+ print(f" == Exception taken from {spsr.M.name} ==")
+ el = spsr.M >> 2
+ print(f" SPSR = {spsr}")
+ print(f" ELR = {addr(elr)}" + (f" (0x{elr_phys:x})" if elr_phys else ""))
+ print(f" ESR = {esr}")
+ print(f" FAR = {addr(ctx.far)}" + (f" (0x{far_phys:x})" if far_phys else ""))
+ print(f" SP = {ctx.sp:#x}" + (f" (0x{sp_phys:x})" if sp_phys else ""))
+
+ for i in range(0, 31, 4):
+ j = min(30, i + 3)
+ print(f" {f'x{i}-x{j}':>7} = {' '.join(f'{r:016x}' for r in ctx.regs[i:j + 1])}")
+
+ if elr_phys:
+ v = self.asc.p.read32(elr_phys)
+
+ print()
+ if v == 0xabad1dea:
+ print(" == Faulting code is not available ==")
+ else:
+ print(" == Faulting code ==")
+ dist = 16
+ self.asc.u.disassemble_at(elr_phys - dist * 4, (dist * 2 + 1) * 4, elr_phys)
+
+ print()
+
+ def Cstr(self, entry):
+ print(f"Message {entry.payload.id}: {entry.payload.string}")
+ print()
+
+ def Ctim(self, entry):
+ print(f"Crash time: {entry.payload.time:#x}")
+ print()
+
+ def Cmbx(self, entry):
+ print(f"Mailbox log (type {entry.payload.type}, index {entry.payload.index}):")
+ for i, msg in enumerate(entry.payload.messages):
+ print(f" #{i:3d} @{msg.timestamp:#10x} ep={msg.endpoint:#4x} {msg.message:#18x}")
+ print()
+
+ def CLHE(self, entry):
+ pass
+
+ def dump(self):
+ print("### Crash dump:")
+ print()
+ for entry in self.data.entries:
+ getattr(self, entry.type, self.default)(entry)
+
+class ASCCrashLogEndpoint(ASCBaseEndpoint):
+ SHORT = "crash"
+ BASE_MESSAGE = CrashLogMessage
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.iobuffer = None
+ self.iobuffer_dva = None
+ self.started = False
+
+ @msg_handler(0x1)
+ def Handle(self, msg):
+ if self.started:
+ return self.handle_crashed(msg)
+ else:
+ return self.handle_getbuf(msg)
+
+ def handle_getbuf(self, msg):
+ size = align(0x1000 * msg.SIZE, 0x4000)
+
+ if msg.DVA:
+ self.iobuffer_dva = msg.DVA
+ self.log(f"buf prealloc at dva {self.iobuffer_dva:#x}")
+ else:
+ self.iobuffer, self.iobuffer_dva = self.asc.ioalloc(size)
+ self.log(f"buf {self.iobuffer:#x} / {self.iobuffer_dva:#x}")
+ self.send(CrashLogMessage(TYPE=1, SIZE=size // 0x1000, DVA=self.iobuffer_dva))
+
+ self.started = True
+ return True
+
+ def crash_soft(self):
+ self.send(0x40)
+
+ def crash_hard(self):
+ self.send(0x22)
+
+ def handle_crashed(self, msg):
+ size = 0x1000 * msg.SIZE
+
+ self.log(f"Crashed!")
+ crashdata = self.asc.ioread(msg.DVA, size)
+ open("crash.bin", "wb").write(crashdata)
+ clog = CrashLogParser(crashdata, self.asc)
+ clog.dump()
+ raise Exception("ASC crashed!")
+
+ return True
+
+if __name__ == "__main__":
+ import sys
+ crashdata = open(sys.argv[1], "rb").read()
+ clog = CrashLogParser(crashdata)
+ clog.dump()
diff --git a/tools/proxyclient/m1n1/fw/asc/ioreporting.py b/tools/proxyclient/m1n1/fw/asc/ioreporting.py
new file mode 100644
index 0000000..f81b6c6
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/asc/ioreporting.py
@@ -0,0 +1,56 @@
+# SPDX-License-Identifier: MIT
+from .base import *
+from ...utils import *
+
+class IOReportingMessage(Register64):
+ TYPE = 63, 52
+
+class IOReporting_GetBuf(IOReportingMessage):
+ TYPE = 63, 52, Constant(1)
+ SIZE = 51, 44
+ DVA = 43, 0
+
+class IOReporting_Start(IOReportingMessage):
+ TYPE = 63, 52, Constant(0xc)
+
+class IOReporting_Report(IOReportingMessage):
+ TYPE = 63, 52, Constant(0x8)
+
+class ASCIOReportingEndpoint(ASCBaseEndpoint):
+ BASE_MESSAGE = IOReportingMessage
+ SHORT = "iorep"
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.iobuffer = None
+ self.iobuffer_dva = None
+
+ @msg_handler(1, IOReporting_GetBuf)
+ def GetBuf(self, msg):
+ if self.iobuffer:
+ self.log("WARNING: trying to reset iobuffer!")
+
+ self.bufsize = align(0x1000 * msg.SIZE, 0x4000)
+
+ if msg.DVA != 0:
+ self.iobuffer = self.iobuffer_dva = msg.DVA
+ self.log(f"buf {self.iobuffer:#x} / {self.iobuffer_dva:#x}")
+ else:
+ self.iobuffer, self.iobuffer_dva = self.asc.ioalloc(self.bufsize)
+ self.log(f"buf {self.iobuffer:#x} / {self.iobuffer_dva:#x}")
+ self.send(IOReporting_GetBuf(DVA=self.iobuffer_dva, SIZE=self.bufsize // 0x1000))
+
+ return True
+
+ @msg_handler(0xc, IOReporting_Start)
+ def Start(self, msg):
+ self.log("start")
+ return True
+
+ @msg_handler(8, IOReporting_Report)
+ def Init(self, msg):
+ self.log("report!")
+ buf = self.asc.iface.readmem(self.iobuffer, self.bufsize)
+ #chexdump(buf)
+ self.send(IOReporting_Report())
+ return True
diff --git a/tools/proxyclient/m1n1/fw/asc/kdebug.py b/tools/proxyclient/m1n1/fw/asc/kdebug.py
new file mode 100644
index 0000000..32a433f
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/asc/kdebug.py
@@ -0,0 +1,57 @@
+# SPDX-License-Identifier: MIT
+from .base import *
+from ...utils import *
+
+class KDebugMessage(Register64):
+ TYPE = 55, 48
+
+class KDebugGetBufMessage(KDebugMessage):
+ TYPE = 55, 48, Constant(1)
+ COUNT = 47, 0
+
+class KDebugPreallocBuf1Message(KDebugMessage):
+ TYPE = 55, 48, Constant(2)
+ DVA = 47, 12
+ FLAGS = 11, 0
+
+class KDebugPreallocBuf2Message(KDebugMessage):
+ TYPE = 55, 48, Constant(3)
+ DVA = 47, 0
+
+class KDebugSendBufMessage(KDebugMessage):
+ TYPE = 55, 48
+ DVA = 47, 0
+
+class KDebugStart(KDebugMessage):
+ TYPE = 55, 48, Constant(8)
+
+class ASCKDebugEndpoint(ASCBaseEndpoint):
+ SHORT = "kdebug"
+ BASE_MESSAGE = KDebugMessage
+
+ @msg_handler(1, KDebugGetBufMessage)
+ def GetBuf(self, msg):
+ size = align_up(msg.COUNT * 0x20, 0x4000)
+ self.iobuffer0, self.iobuffer0_iova = self.asc.ioalloc(size)
+ self.send(KDebugSendBufMessage(TYPE=1, DVA=self.iobuffer0_iova))
+
+ self.iobuffer1, self.iobuffer1_iova = self.asc.ioalloc(0x2000)
+ self.send(KDebugSendBufMessage(TYPE=2, DVA=self.iobuffer1_iova))
+ return True
+
+ @msg_handler(2, KDebugPreallocBuf1Message)
+ def SetBuf1(self, msg):
+ #self.send(KDebugSendBufMessage(TYPE=1, DVA=msg.DVA))
+ return True
+
+ @msg_handler(3, KDebugPreallocBuf2Message)
+ def SetBuf2(self, msg):
+ #self.send(KDebugSendBufMessage(TYPE=2, DVA=msg.DVA))
+ return True
+
+ def start(self):
+ self.iobuffer0 = None
+ self.iobuffer1 = None
+ self.iobuffer0_iova = None
+ self.iobuffer1_iova = None
+ self.send(KDebugStart())
diff --git a/tools/proxyclient/m1n1/fw/asc/mgmt.py b/tools/proxyclient/m1n1/fw/asc/mgmt.py
new file mode 100644
index 0000000..162fcd2
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/asc/mgmt.py
@@ -0,0 +1,144 @@
+# SPDX-License-Identifier: MIT
+import time
+
+from .base import *
+from ...utils import *
+
+## Management endpoint
+class ManagementMessage(Register64):
+ TYPE = 59, 52
+
+class Mgmt_Hello(ManagementMessage):
+ TYPE = 59, 52, Constant(1)
+ MAX_VER = 31, 16
+ MIN_VER = 15, 0
+
+class Mgmt_HelloAck(ManagementMessage):
+ TYPE = 59, 52, Constant(2)
+ MAX_VER = 31, 16
+ MIN_VER = 15, 0
+
+class Mgmt_Ping(ManagementMessage):
+ TYPE = 59, 52, Constant(3)
+
+class Mgmt_Pong(ManagementMessage):
+ TYPE = 59, 52, Constant(4)
+
+class Mgmt_StartEP(ManagementMessage):
+ TYPE = 59, 52, Constant(5)
+ EP = 39, 32
+ FLAG = 1, 0
+
+class Mgmt_SetIOPPower(ManagementMessage):
+ TYPE = 59, 52, Constant(6)
+ STATE = 15, 0
+
+class Mgmt_IOPPowerAck(ManagementMessage):
+ TYPE = 59, 52, Constant(7)
+ STATE = 15, 0
+
+class Mgmt_EPMap(ManagementMessage):
+ TYPE = 59, 52, Constant(8)
+ LAST = 51
+ BASE = 34, 32
+ BITMAP = 31, 0
+
+class Mgmt_EPMap_Ack(ManagementMessage):
+ TYPE = 59, 52, Constant(8)
+ LAST = 51
+ BASE = 34, 32
+ MORE = 0
+
+class Mgmt_SetAPPower(ManagementMessage):
+ TYPE = 59, 52, Constant(0xb)
+ STATE = 15, 0
+
+class ASCManagementEndpoint(ASCBaseEndpoint):
+ BASE_MESSAGE = ManagementMessage
+ SHORT = "mgmt"
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.syslog_started = False
+ self.iop_power_state = 0
+ self.ap_power_state = 0
+ self.verbose = 1
+
+ @msg_handler(1, Mgmt_Hello)
+ def Hello(self, msg):
+ self.log(f"Supported versions {msg.MIN_VER} .. {msg.MAX_VER}")
+ # FIXME: we pick the highest version, we should negotiate
+ self.send(Mgmt_HelloAck(MIN_VER=msg.MAX_VER, MAX_VER=msg.MAX_VER))
+ return True
+
+ @msg_handler(8, Mgmt_EPMap)
+ def EPMap(self, msg):
+ for i in range(32):
+ if msg.BITMAP & (1 << i):
+ epno = 32 * msg.BASE + i
+ self.asc.eps.append(epno)
+ if self.verbose > 0:
+ self.log(f"Adding endpoint {epno:#x}")
+
+ self.send(Mgmt_EPMap_Ack(BASE=msg.BASE, LAST=msg.LAST, MORE=0 if msg.LAST else 1))
+
+ if msg.LAST:
+ for ep in self.asc.eps:
+ if ep == 0: continue
+ if ep < 0x10:
+ self.asc.start_ep(ep)
+ self.boot_done()
+
+ return True
+
+ @msg_handler(0xb, Mgmt_SetAPPower)
+ def APPowerAck(self, msg):
+ if self.verbose > 0:
+ self.log(f"AP power state is now {msg.STATE:#x}")
+ self.ap_power_state = msg.STATE
+ return True
+
+ @msg_handler(7, Mgmt_IOPPowerAck)
+ def IOPPowerAck(self, msg):
+ if self.verbose > 0:
+ self.log(f"IOP power state is now {msg.STATE:#x}")
+ self.iop_power_state = msg.STATE
+ return True
+
+ @msg_handler(4, Mgmt_Pong)
+ def Pong(self, msg):
+ return True
+
+ def start(self):
+ self.log("Starting via message")
+ self.send(Mgmt_SetIOPPower(STATE=0x220))
+
+ def wait_boot(self, timeout=None):
+ if timeout is not None:
+ timeout += time.time()
+ while self.iop_power_state != 0x20 or self.ap_power_state != 0x20:
+ self.asc.work()
+ if timeout and time.time() > timeout:
+ raise ASCTimeout("Boot timed out")
+ self.log("Startup complete")
+
+ def start_ep(self, epno):
+ self.send(Mgmt_StartEP(EP=epno, FLAG=2))
+
+ def stop_ep(self, epno):
+ self.send(Mgmt_StartEP(EP=epno, FLAG=1))
+
+ def boot_done(self):
+ self.send(Mgmt_SetAPPower(STATE=0x20))
+
+ def ping(self):
+ self.send(Mgmt_Ping())
+
+ def stop(self, state=0x10):
+ self.log("Stopping via message")
+ self.send(Mgmt_SetAPPower(STATE=0x10))
+ while self.ap_power_state == 0x20:
+ self.asc.work()
+ self.send(Mgmt_SetIOPPower(STATE=state))
+ while self.iop_power_state != state:
+ self.asc.work()
diff --git a/tools/proxyclient/m1n1/fw/asc/oslog.py b/tools/proxyclient/m1n1/fw/asc/oslog.py
new file mode 100644
index 0000000..b1a360b
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/asc/oslog.py
@@ -0,0 +1,30 @@
+# SPDX-License-Identifier: MIT
+from .base import *
+from ...utils import *
+
+## OSLog endpoint
+
+class OSLogMessage(Register64):
+ TYPE = 63, 56
+
+class OSLog_Init(OSLogMessage):
+ TYPE = 63, 56, Constant(1)
+ UNK = 51, 0
+
+class OSLog_Ack(OSLogMessage):
+ TYPE = 63, 56, Constant(3)
+
+class ASCOSLogEndpoint(ASCBaseEndpoint):
+ BASE_MESSAGE = OSLogMessage
+ SHORT = "oslog"
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.started = False
+
+ @msg_handler(1, OSLog_Init)
+ def Init(self, msg):
+ self.log(f"oslog init: {msg.UNK:#x}")
+ self.send(OSLog_Ack())
+ self.started = True
+ return True
diff --git a/tools/proxyclient/m1n1/fw/asc/syslog.py b/tools/proxyclient/m1n1/fw/asc/syslog.py
new file mode 100644
index 0000000..3387c27
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/asc/syslog.py
@@ -0,0 +1,72 @@
+# SPDX-License-Identifier: MIT
+import struct
+
+from .base import *
+from ...utils import *
+
+## Syslog endpoint
+
+class SyslogMessage(Register64):
+ TYPE = 59, 52
+
+class Syslog_Init(SyslogMessage):
+ TYPE = 59, 52, Constant(8)
+ ENTRYSIZE = 39, 24
+ COUNT = 15, 0
+
+class Syslog_GetBuf(SyslogMessage):
+ TYPE = 59, 52, Constant(1)
+ SIZE = 51, 44
+ DVA = 43, 0
+
+class Syslog_Log(SyslogMessage):
+ TYPE = 59, 52, Constant(5)
+ INDEX = 7, 0
+
+class ASCSysLogEndpoint(ASCBaseEndpoint):
+ BASE_MESSAGE = SyslogMessage
+ SHORT = "syslog"
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.entrysize = None
+ self.count = None
+ self.iobuffer = None
+ self.iobuffer_dva = None
+ self.started = False
+
+ @msg_handler(8, Syslog_Init)
+ def Init(self, msg):
+ self.entrysize = msg.ENTRYSIZE
+ self.count = msg.COUNT
+ self.log(f"count {self.count}, entrysize {self.entrysize}")
+ return True
+
+ @msg_handler(1, Syslog_GetBuf)
+ def GetBuf(self, msg):
+ size = align(0x1000 * msg.SIZE, 0x4000)
+
+ if self.iobuffer:
+ print("WARNING: trying to reset iobuffer!")
+
+ if msg.DVA:
+ self.iobuffer_dva = msg.DVA
+ self.log(f"buf prealloc at dva {self.iobuffer_dva:#x}")
+ else:
+ self.iobuffer, self.iobuffer_dva = self.asc.ioalloc(size)
+ self.log(f"buf {self.iobuffer:#x} / {self.iobuffer_dva:#x}")
+ self.send(Syslog_GetBuf(SIZE=size // 0x1000, DVA=self.iobuffer_dva))
+
+ self.started = True
+ return True
+
+ @msg_handler(5, Syslog_Log)
+ def Log(self, msg):
+ stride = 0x20 + self.entrysize
+ log = self.asc.ioread(self.iobuffer_dva + msg.INDEX * stride, stride)
+ hdr, unk, context, logmsg = struct.unpack(f"<II24s{self.entrysize}s", log)
+ context = context.split(b"\x00")[0].decode("ascii")
+ logmsg = logmsg.split(b"\x00")[0].decode("ascii").rstrip("\n")
+ self.log(f"* [{context}]{logmsg}")
+ self.send(msg)
+ return True
diff --git a/tools/proxyclient/m1n1/fw/common.py b/tools/proxyclient/m1n1/fw/common.py
new file mode 100644
index 0000000..479e2df
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/common.py
@@ -0,0 +1,170 @@
+# SPDX-License-Identifier: MIT
+
+from dataclasses import dataclass
+from enum import IntEnum
+from m1n1.utils import *
+from construct import *
+
+uint8_t = Int8ul
+int16_t = Int16sl
+uint16_t = Int16ul
+int32_t = Int32sl
+uint32_t = Int32ul
+int64_t = Int64sl
+uint64_t = Int64ul
+
+uint = uint32_t
+int_ = int32_t
+ulong = uint64_t
+long_ = int64_t
+
+def Bool(c):
+ return ExprAdapter(c, lambda d, ctx: bool(d & 1), lambda d, ctx: int(d))
+
+def SizedArray(count, svar, subcon):
+ return Padded(subcon.sizeof() * count, Array(lambda ctx: min(count, ctx.get(svar, ctx._.get(svar))), subcon))
+
+def SizedBytes(count, svar):
+ return Lazy(Padded(count, Bytes(lambda ctx: ctx.get(svar) or ctx._.get(svar))))
+
+def UnkBytes(s):
+ return Default(HexDump(Bytes(s)), b"\x00" * s)
+
+bool_ = Bool(Int8ul)
+
+class OSObject(Construct):
+ TYPE = None
+
+ def _parse(self, stream, context, path, recurse=False):
+ tag = stream.read(1).decode("ascii")
+ if not recurse and self.TYPE is not None and self.TYPE != tag:
+ raise Exception("Object type mismatch")
+
+ if tag == "d":
+ count = Int32ul.parse_stream(stream)
+ d = {}
+ for i in range(count):
+ k = self._parse(stream, context, path, True)
+ v = self._parse(stream, context, path, True)
+ d[k] = v
+ return d
+ elif tag == "n":
+ return Int64ul.parse_stream(stream)
+ elif tag == "s":
+ length = Int32ul.parse_stream(stream)
+ s = stream.read(length).decode("utf-8")
+ assert stream.read(1) == b'\0'
+ return s
+ else:
+ raise Exception(f"Unknown object tag {tag!r}")
+
+ def _build(self, obj, stream, context, path):
+ assert False
+
+ def _sizeof(self, context, path):
+ return None
+
+class OSDictionary(OSObject):
+ TYPE = 'd'
+
+class OSSerialize(Construct):
+ def _parse(self, stream, context, path, recurse=False):
+ hdr = Int32ul.parse_stream(stream)
+ if hdr != 0xd3:
+ raise Exception("Bad header")
+
+ obj, last = self.parse_obj(stream)
+ assert last
+ return obj
+
+ def parse_obj(self, stream, level=0):
+ # align to 32 bits
+ pos = stream.tell()
+ if pos & 3:
+ stream.read(4 - (pos & 3))
+
+ tag = Int32ul.parse_stream(stream)
+
+ last = bool(tag & 0x80000000)
+ otype = (tag >> 24) & 0x1f
+ size = tag & 0xffffff
+
+ #print(f"{' '*level} @{stream.tell():#x} {otype} {last} {size}")
+
+ if otype == 1:
+ d = {}
+ for i in range(size):
+ k, l = self.parse_obj(stream, level + 1)
+ assert not l
+ v, l = self.parse_obj(stream, level + 1)
+ assert l == (i == size - 1)
+ d[k] = v
+ elif otype == 2:
+ d = []
+ for i in range(size):
+ v, l = self.parse_obj(stream, level + 1)
+ assert l == (i == size - 1)
+ d.append(v)
+ elif otype == 4:
+ d = Int64ul.parse_stream(stream)
+ elif otype == 9:
+ d = stream.read(size).decode("utf-8")
+ elif otype == 10:
+ d = stream.read(size)
+ elif otype == 11:
+ d = bool(size)
+ else:
+ raise Exception(f"Unknown tag {otype}")
+
+ #print(f"{' '*level} => {d}")
+ return d, last
+
+ def build_obj(self, obj, stream, last=True, level=0):
+ tag = 0
+ if last:
+ tag |= 0x80000000
+
+ if isinstance(obj, dict):
+ tag |= (1 << 24) | len(obj)
+ Int32ul.build_stream(tag, stream)
+ for i, (k, v) in enumerate(obj.items()):
+ self.build_obj(k, stream, False, level + 1)
+ self.build_obj(v, stream, i == len(obj) - 1, level + 1)
+ elif isinstance(obj, list):
+ tag |= (2 << 24) | len(obj)
+ Int32ul.build_stream(tag, stream)
+ for i, v in enumerate(obj):
+ self.build_obj(v, stream, i == len(obj) - 1, level + 1)
+ elif isinstance(obj, int):
+ tag |= (4 << 24) | 64
+ Int32ul.build_stream(tag, stream)
+ Int64ul.build_stream(obj, stream)
+ elif isinstance(obj, str):
+ obj = obj.encode("utf-8")
+ tag |= (9 << 24) | len(obj)
+ Int32ul.build_stream(tag, stream)
+ stream.write(obj)
+ elif isinstance(obj, bytes):
+ tag |= (10 << 24) | len(obj)
+ Int32ul.build_stream(tag, stream)
+ stream.write(obj)
+ elif isinstance(obj, bool):
+ tag |= (11 << 24) | int(obj)
+ Int32ul.build_stream(tag, stream)
+ else:
+ raise Exception(f"Cannot encode {obj!r}")
+
+ pos = stream.tell()
+ if pos & 3:
+ stream.write(bytes(4 - (pos & 3)))
+
+ def _build(self, obj, stream, context, path):
+ Int32ul.build_stream(0xd3, stream)
+ self.build_obj(obj, stream)
+
+ def _sizeof(self, context, path):
+ return None
+
+def string(size):
+ return Padded(size, CString("utf8"))
+
diff --git a/tools/proxyclient/m1n1/fw/dcp/__init__.py b/tools/proxyclient/m1n1/fw/dcp/__init__.py
new file mode 100644
index 0000000..e77f6cf
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/dcp/__init__.py
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: MIT
+
diff --git a/tools/proxyclient/m1n1/fw/dcp/client.py b/tools/proxyclient/m1n1/fw/dcp/client.py
new file mode 100644
index 0000000..941ab20
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/dcp/client.py
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: MIT
+from ...utils import *
+
+from ..asc import StandardASC
+from .dcpep import DCPEndpoint
+
+class DCPClient(StandardASC):
+ ENDPOINTS = {
+ 0x37: DCPEndpoint,
+ }
+
+ def __init__(self, u, asc_base, dart=None, disp_dart=None):
+ super().__init__(u, asc_base, dart)
+ self.disp_dart = disp_dart
diff --git a/tools/proxyclient/m1n1/fw/dcp/dcpav.py b/tools/proxyclient/m1n1/fw/dcp/dcpav.py
new file mode 100644
index 0000000..d063c6c
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/dcp/dcpav.py
@@ -0,0 +1,110 @@
+# SPDX-License-Identifier: MIT
+from construct import *
+
+from ...utils import *
+from ..asc import StandardASC
+from ..afk.epic import *
+
+class DCPAVControllerService(EPICStandardService):
+ NAME = "dcpav-controller-epic"
+ SHORT = "dcpav"
+
+ def setPower(self, power):
+ self.call(8, 0x8, struct.pack("<16xI12x", power))
+
+ def getPower(self, power):
+ return struct.unpack("<16xI12x", self.call(8, 0x9, bytes(32)))
+
+ def wakeDisplay(self):
+ self.call(8, 0xa, bytes(16))
+
+ def sleepDisplay(self):
+ self.call(8, 0xb, bytes(16))
+
+ def forceHotPlugDetect(self):
+ self.call(8, 0xc, bytes(16))
+
+ def setVirtualDeviceMode(self, mode):
+ self.call(8, 0xd, struct.pack("<16xI12x", mode))
+
+class DCPDPControllerService(EPICStandardService):
+ NAME = "dcpdp-controller-epic"
+ SHORT = "dcpdp"
+
+class DCPDPTXEndpoint(EPICEndpoint):
+ SHORT = "dptx"
+
+ SERVICES = [
+ DCPAVControllerService,
+ DCPDPControllerService,
+ ]
+
+ATC0 = 0
+ATC1 = 1
+ATC2 = 2
+ATC3 = 3
+LPDPTX = 4
+DPTX = 5
+
+DPPHY = 0
+DPIN0 = 1
+DPIN1 = 2
+
+class DCPDPTXRemotePortService(EPICStandardService):
+ NAME = "dcpdptx-port-epic"
+ SHORT = "port"
+
+ def displayRequest(self):
+ self.call(8, 8, bytes(16))
+
+ def displayRelease(self):
+ self.call(8, 9, bytes(16))
+
+ def connectTo(self, connected, unit, port, unk=0):
+ target = 0
+ if connected:
+ target |= (1 << 8)
+ target |= unit
+ target |= port << 4
+ self.call(8, 13, struct.pack("<16xII8x", unk, target))
+
+class DCPDPTXPortEndpoint(EPICEndpoint):
+ SHORT = "dpport"
+
+ SERVICES = [
+ DCPDPTXRemotePortService,
+ DCPDPControllerService,
+ ]
+
+class DCPDPDevice(EPICStandardService):
+ NAME = "dcpav-device-epic"
+ SHORT = "dpdev"
+
+class DCPAVDeviceEndpoint(EPICEndpoint):
+ SHORT = "avdev"
+
+ SERVICES = [
+ DCPDPDevice,
+ ]
+
+class DCPDPService(EPICStandardService):
+ NAME = "dcpav-service-epic"
+ SHORT = "dpserv"
+
+class DCPAVServiceEndpoint(EPICEndpoint):
+ SHORT = "avserv"
+
+ SERVICES = [
+ DCPDPService,
+ ]
+
+class DCPAVSimpleVideoInterface(EPICStandardService):
+ NAME = "dcpav-video-interface-epic"
+ SHORT = "video"
+
+class DCPAVVideoEndpoint(EPICEndpoint):
+ SHORT = "avserv"
+
+ SERVICES = [
+ DCPAVSimpleVideoInterface,
+ ]
diff --git a/tools/proxyclient/m1n1/fw/dcp/dcpep.py b/tools/proxyclient/m1n1/fw/dcp/dcpep.py
new file mode 100644
index 0000000..6e4f250
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/dcp/dcpep.py
@@ -0,0 +1,163 @@
+# SPDX-License-Identifier: MIT
+import struct
+from dataclasses import dataclass
+from enum import IntEnum
+
+from ..asc.base import *
+from ...utils import *
+
+## DCP main endpoint
+
+class DCPMessage(Register64):
+ TYPE = 3, 0
+
+class DCPEp_SetShmem(DCPMessage):
+ DVA = 63, 16
+ FLAG = 7, 4
+ TYPE = 3, 0, Constant(0)
+
+class DCPEp_InitComplete(DCPMessage):
+ TYPE = 3, 0, Constant(1)
+
+class CallContext(IntEnum):
+ CB = 0
+ CMD = 2
+ ASYNC = 3
+ OOBCB = 4
+ OOBCMD = 6
+
+class DCPEp_Msg(DCPMessage):
+ LEN = 63, 32
+ OFF = 31, 16
+ CTX = 11, 8, CallContext
+ ACK = 6
+ TYPE = 3, 0, Constant(2)
+
+@dataclass
+class DCPCallState:
+ tag: str
+ off: int
+ in_len: int
+ in_data: bytes
+ out_addr: int
+ out_len: int
+ complete: bool = False
+
+class DCPCallChannel(Reloadable):
+ def __init__(self, dcpep, name, buf, bufsize):
+ self.dcp = dcpep
+ self.name = name
+ self.buf = buf
+ self.bufsize = bufsize
+ self.off = 0
+ self.pending = []
+
+ def ack(self):
+ if not self.pending:
+ raise Exception("ACK with no calls pending")
+
+ self.pending[-1].complete = True
+
+ def call(self, ctx, tag, inbuf, out_len):
+ in_len = len(inbuf)
+ data = tag.encode("ascii")[::-1] + struct.pack("<II", in_len, out_len) + inbuf
+ data_size = len(data) + out_len
+ assert (self.off + data_size) <= self.bufsize
+
+ self.dcp.asc.iface.writemem(self.dcp.shmem + self.buf + self.off, data)
+
+ state = DCPCallState(off=self.off, tag=tag, in_len=in_len, in_data=data, out_len=out_len,
+ out_addr=self.buf + self.off + 12 + in_len)
+
+ self.off += align_up(data_size, 0x40)
+ self.pending.append(state)
+
+ print(f"len={data_size:#x} {in_len}")
+ self.dcp.send(DCPEp_Msg(LEN=data_size, OFF=state.off, CTX=ctx, ACK=0))
+
+ while not state.complete:
+ self.dcp.asc.work()
+
+ print(f"off={state.out_addr:#x} len={out_len}")
+ out_data = self.dcp.asc.iface.readmem(self.dcp.shmem + state.out_addr, out_len)
+
+ assert self.pending.pop() is state
+ self.off = state.off
+
+ return out_data
+
+class DCPCallbackChannel(Reloadable):
+ def __init__(self, dcpep, name, buf, bufsize):
+ self.dcp = dcpep
+ self.name = name
+ self.buf = buf
+ self.bufsize = bufsize
+ self.pending = []
+
+ def cb(self, msg):
+ data = self.dcp.asc.iface.readmem(self.dcp.shmem + self.buf + msg.OFF, msg.LEN)
+ tag = data[:4][::-1].decode("ascii")
+ in_len, out_len = struct.unpack("<II", data[4:12])
+ in_data = data[12:12 + in_len]
+
+ state = DCPCallState(off=msg.OFF, tag=tag, in_len=in_len, out_len=out_len,
+ in_data=in_data, out_addr=self.buf + msg.OFF + 12 + in_len)
+
+ self.pending.append(state)
+
+ out_data = self.dcp.mgr.handle_cb(state)
+ self.dcp.asc.iface.writemem(self.dcp.shmem + state.out_addr, out_data)
+ self.dcp.send(DCPEp_Msg(CTX=msg.CTX, ACK=1))
+
+ assert self.pending.pop() is state
+
+
+class DCPEndpoint(ASCBaseEndpoint):
+ BASE_MESSAGE = DCPMessage
+ SHORT = "dcpep"
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.shmem = self.shmem_dva = None
+ self.init_complete = False
+ self.mgr = None
+
+ self.ch_cb = DCPCallbackChannel(self, "CB", 0x60000, 0x8000)
+ self.ch_cmd = DCPCallChannel(self, "CMD", 0, 0x8000)
+ self.ch_async = DCPCallbackChannel(self, "ASYNC", 0x40000, 0x20000)
+ self.ch_oobcb = DCPCallbackChannel(self, "OOBCB", 0x68000, 0x8000)
+ self.ch_oobcmd = DCPCallChannel(self, "OOBCMD", 0x8000, 0x8000)
+
+ @msg_handler(2, DCPEp_Msg)
+ def Rx(self, msg):
+ if msg.ACK:
+ if msg.CTX in (CallContext.CMD, CallContext.CB):
+ self.ch_cmd.ack()
+ elif msg.CTX in (CallContext.OOBCMD, CallContext.OOBCB):
+ self.ch_oobcmd.ack()
+ else:
+ raise Exception(f"Unknown RX ack channel {msg.CTX}")
+ else:
+ if msg.CTX == CallContext.CB:
+ self.ch_cb.cb(msg)
+ elif msg.CTX == CallContext.OOBCMD:
+ self.ch_oobcb.cb(msg)
+ elif msg.CTX == CallContext.ASYNC:
+ self.ch_async.cb(msg)
+ else:
+ raise Exception(f"Unknown RX callback channel {msg.CTX}")
+ return True
+
+ @msg_handler(1, DCPEp_InitComplete)
+ def InitComplete(self, msg):
+ self.log("init complete")
+ self.init_complete = True
+ return True
+
+ def initialize(self):
+ self.shmem, self.shmem_dva = self.asc.ioalloc(0x100000)
+ self.asc.p.memset32(self.shmem, 0, 0x100000)
+ self.send(DCPEp_SetShmem(DVA=self.shmem_dva))
+ while not self.init_complete:
+ self.asc.work()
+
diff --git a/tools/proxyclient/m1n1/fw/dcp/iboot.py b/tools/proxyclient/m1n1/fw/dcp/iboot.py
new file mode 100644
index 0000000..8124ca9
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/dcp/iboot.py
@@ -0,0 +1,215 @@
+# SPDX-License-Identifier: MIT
+from construct import *
+
+from ...utils import *
+from ..asc import StandardASC
+from ..afk.epic import *
+from .dcpav import *
+
+EOTF = "EOTF" / Enum(Int32ul,
+ GAMMA_SDR = 1,
+ GAMMA_HDR = 2,
+)
+
+Encoding = "Encoding" / Enum(Int32ul,
+ RGB = 1,
+ YCBCR_444 = 3,
+ YCBCR_422 = 4,
+ YCBCR_420 = 5,
+)
+
+Colorimetry = "Colorimetry" / Enum(Int32ul,
+ BT601_709 = 1,
+ BT2020 = 2,
+ DCIP3 = 3,
+)
+
+Colorspace = "Colorspace" / Enum(Int32ul,
+ SRGB = 1,
+ Native = 2,
+ BT2020 = 3,
+)
+
+SurfaceFormat = "SurfaceFormat" / Enum(Int32ul,
+ BGRA = 1,
+ BGRA2 = 2,
+ RGBA = 3,
+ w18p = 4,
+ BGRA3 = 5,
+ _444v = 6,
+ _422v = 7,
+ _420v = 8,
+ w30r = 9,
+ w40a = 10,
+)
+
+Transform = "Transform" / Enum(Int8ul,
+ NONE = 0,
+ XFLIP = 1,
+ YFLIP = 2,
+ ROT_90 = 3,
+ ROT_180 = 4,
+ ROT_270 = 5,
+)
+
+AddrFormat = "AddrFormat" / Enum(Int32ul,
+ PLANAR = 1,
+ TILED = 2,
+ AGX = 3
+)
+
+TimingMode = Struct(
+ "valid" / Bool(Int32ul),
+ "width" / Int32ul,
+ "height" / Int32ul,
+ "fps_frac" / Int16ul,
+ "fps_int" / Int16ul,
+ Padding(8),
+)
+
+TimingModeList = Struct(
+ "count" / Int32ul,
+ "list" / GreedyRange(TimingMode),
+)
+
+ColorMode = Struct(
+ "valid" / Bool(Int32ul),
+ "colorimetry" / Colorimetry,
+ "eotf" / EOTF,
+ "encoding" / Encoding,
+ "bpp" / Int32ul,
+ "unk" / Int32ul,
+)
+
+ColorModeList = Struct(
+ "count" / Int32ul,
+ "list" / GreedyRange(ColorMode),
+)
+
+SwapInfo = Struct(
+ "unk1" / Int32ul,
+ "unk2" / Int32ul,
+ "unk3" / Int32ul,
+ "swap_id" / Int32ul,
+ "unk5" / Int32ul,
+)
+
+IBootPlaneInfo = Struct(
+ "unk1" / Default(Int32ul, 0),
+ "addr" / Default(Int64ul, 0),
+ "tile_size" / Default(Int32ul, 0),
+ "stride" / Default(Int32ul, 0),
+ "unk5" / Default(Int32ul, 0),
+ "unk6" / Default(Int32ul, 0),
+ "unk7" / Default(Int32ul, 0),
+ "unk8" / Default(Int32ul, 0),
+ "addr_format" / Default(AddrFormat, 0),
+ "unk9" / Default(Int32ul, 0),
+)
+
+IBootLayerInfo = Struct(
+ "planes" / Array(3, IBootPlaneInfo),
+ "unk" / Default(Int32ul, 0),
+ "plane_cnt" / Int32ul,
+ "width" / Int32ul,
+ "height" / Int32ul,
+ "surface_fmt" / SurfaceFormat,
+ "colorspace" / Colorspace,
+ "eotf" / EOTF,
+ "transform" / Transform,
+ Padding(3)
+)
+
+SwapSetLayer = Struct(
+ "unk" / Default(Int32ul, 0),
+ "layer_id" / Int32ul,
+ "layer_info" / IBootLayerInfo,
+ "src_w" / Int32ul,
+ "src_h" / Int32ul,
+ "src_x" / Int32ul,
+ "src_y" / Int32ul,
+ "dst_w" / Int32ul,
+ "dst_h" / Int32ul,
+ "dst_x" / Int32ul,
+ "dst_y" / Int32ul,
+ "unk2" / Default(Int32ul, 0),
+)
+
+class DCPIBootService(EPICService):
+ NAME = "disp0-service"
+ SHORT = "disp0"
+
+ def send_cmd(self, op, data=b'', replen=None):
+ msg = struct.pack("<IIII", op, 16 + len(data), 0, 0) + data
+ if replen is not None:
+ replen += 8
+ resp = super().send_cmd(0xc0, msg, replen)
+ if not resp:
+ return
+ rcmd, rlen = struct.unpack("<II", resp[:8])
+ return resp[8:rlen]
+
+ def setPower(self, power):
+ self.send_cmd(2, b"\x01" if power else b"\x00")
+
+ def getModeCount(self):
+ buf = self.send_cmd(3, b"", 12)
+ hpd, timing_cnt, color_cnt = struct.unpack("<B3xII", buf)
+ return bool(hpd), timing_cnt, color_cnt
+
+ def getTimingModes(self):
+ return TimingModeList.parse(self.send_cmd(4, replen=4096)).list
+
+ def getColorModes(self):
+ return ColorModeList.parse(self.send_cmd(5, replen=4096)).list
+
+ def setMode(self, timing_mode, color_mode):
+ data = TimingMode.build(timing_mode) + ColorMode.build(color_mode)
+ self.send_cmd(6, data)
+
+ def swapBegin(self):
+ return SwapInfo.parse(self.send_cmd(15, replen=128))
+
+ def swapSetLayer(self, layer_id, info, src_rect, dst_rect):
+ data = Container()
+ data.layer_id = layer_id
+ data.layer_info = info
+ data.src_w, data.src_h, data.src_x, data.src_y = src_rect
+ data.dst_w, data.dst_h, data.dst_x, data.dst_y = dst_rect
+ return self.send_cmd(16, SwapSetLayer.build(data), replen=128)
+
+ def swapSetTimestamp(self):
+ pass
+ # 17
+
+ def swapEnd(self):
+ return self.send_cmd(18, b"\x00" * 12, replen=128)
+
+ #def swapWait(self, swap_id):
+ #buf = struct.pack("<IIII", 1, swap_id, 0, swap_id)
+ #return self.send_cmd(19, buf, replen=128)
+
+class DCPIBootEndpoint(EPICEndpoint):
+ SHORT = "iboot"
+
+ SERVICES = [
+ DCPIBootService,
+ ]
+
+
+class DCPIBootClient(StandardASC):
+ DVA_OFFSET = 0xf00000000
+
+ ENDPOINTS = {
+ 0x20: AFKSystemEndpoint,
+ 0x23: DCPIBootEndpoint,
+ 0x24: DCPDPTXEndpoint,
+ 0x2a: DCPDPTXPortEndpoint,
+ 0x27: DCPAVDeviceEndpoint,
+ 0x28: DCPAVServiceEndpoint,
+ 0x29: DCPAVVideoEndpoint,
+ }
+
+ def __init__(self, u, asc_base, dart=None, disp_dart=None):
+ super().__init__(u, asc_base, dart)
+ self.disp_dart = disp_dart
diff --git a/tools/proxyclient/m1n1/fw/dcp/ipc.py b/tools/proxyclient/m1n1/fw/dcp/ipc.py
new file mode 100644
index 0000000..c961a8c
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/dcp/ipc.py
@@ -0,0 +1,789 @@
+# SPDX-License-Identifier: MIT
+
+from dataclasses import dataclass
+import pprint
+from enum import IntEnum
+
+from ..common import *
+from m1n1.utils import *
+from construct import *
+
+@dataclass
+class ByRef:
+ val: object
+
+class Pointer(Subconstruct):
+ pass
+
+class InPtr(Pointer):
+ pass
+
+class OutPtr(Pointer):
+ pass
+
+class InOutPtr(Pointer):
+ pass
+
+class InOut(Subconstruct):
+ pass
+
+Ptr = InOutPtr
+
+class NULL:
+ def __str__(self):
+ return "NULL"
+ def __repr__(self):
+ return "NULL"
+NULL = NULL()
+
+class Method:
+ def __init__(self, rtype, name, *args, **kwargs):
+ self.rtype = rtype
+ self.name = name
+
+ if args and kwargs:
+ raise Exception("Cannot specify args and kwargs")
+ elif args:
+ args = [(f"arg{i}", arg) for i, arg in enumerate(args)]
+ self.as_kwargs = False
+ elif kwargs:
+ args = list(kwargs.items())
+ self.as_kwargs = True
+ else:
+ args = []
+
+ self.args = args
+
+ in_size = 0
+ out_size = 0
+ self.in_fields = []
+ self.out_fields = []
+
+ if rtype is not None:
+ args.append(("ret", rtype))
+
+ self.dir = []
+ self.nullable = []
+ self.array_of_p = []
+
+ for i, (name, field) in enumerate(self.args):
+ align = 1
+
+ pfield = field
+ dir = "in"
+
+ if name == "ret":
+ dir = "out"
+
+ while isinstance(pfield, Subconstruct):
+ if isinstance(pfield, InPtr):
+ dir = "in"
+ elif isinstance(pfield, OutPtr):
+ dir = "out"
+ elif isinstance(pfield, (InOut, InOutPtr)):
+ dir = "inout"
+ pfield = pfield.subcon
+ if isinstance(pfield, FormatField):
+ align = min(4, pfield.length)
+
+ if dir in ("in", "inout"):
+ #if in_size % align:
+ #self.in_fields.append(Padding(align - (in_size % align)))
+ #in_size += align - (in_size % align)
+
+ self.in_fields.append(name / field)
+ in_size += field.sizeof()
+
+ if dir in ("out", "inout"):
+ #if out_size % align:
+ #self.out_fields.append(Padding(align - (out_size % align)))
+ #out_size += align - (out_size % align)
+
+ self.out_fields.append(name / field)
+ out_size += field.sizeof()
+
+ self.dir.append(dir)
+
+ for i, (name, field) in enumerate(self.args):
+ array_size = None
+ array_of_p = False
+ nullable = False
+ pfield = field
+
+ while isinstance(pfield, Subconstruct):
+ if isinstance(pfield, Array) and array_size is None:
+ array_size = pfield.count
+ if isinstance(pfield, Pointer):
+ nullable = True
+ array_of_p = array_size is not None
+ pfield = pfield.subcon
+
+ if nullable:
+ if array_of_p:
+ self.in_fields.append((name + "_null") / bool_[array_size])
+ in_size += array_size
+ else:
+ self.in_fields.append((name + "_null") / bool_)
+ in_size += 1
+
+ self.nullable.append(nullable)
+ self.array_of_p.append(array_of_p)
+
+ if in_size % 4:
+ self.in_fields.append(Padding(4 - (in_size % 4)))
+ if out_size % 4:
+ self.out_fields.append(Padding(4 - (out_size % 4)))
+
+ self.in_struct = Struct(*self.in_fields)
+ self.out_struct = Struct(*self.out_fields)
+
+ def get_field_val(self, i, in_vals, out_vals=None, nullobj=None):
+ name, field = self.args[i]
+
+ nullable = self.nullable[i]
+ array_of_p = self.array_of_p[i]
+
+ val = None
+
+ if out_vals:
+ val = out_vals.get(name, val)
+ if val is None and in_vals:
+ val = in_vals.get(name, val)
+
+ if nullable and val is not None:
+ null = in_vals.get(name + "_null", None)
+ if null is None:
+ return None
+ if not array_of_p:
+ val = nullobj if null else val
+ else:
+ val2 = [nullobj if n else val for val, n in zip(val, null)]
+ if isinstance(val, ListContainer):
+ val2 = ListContainer(val2)
+ val = val2
+
+ return val
+
+ def fmt_args(self, in_vals, out_vals=None):
+ s = []
+
+ for i, (name, field) in enumerate(self.args):
+ if name == "ret":
+ continue
+
+ dir = self.dir[i]
+ nullable = self.nullable[i]
+
+ val = self.get_field_val(i, in_vals, out_vals, nullobj=NULL)
+
+ if val is not None:
+ if self.is_long(val):
+ s.append(f"{name}=...")
+ elif isinstance(val, ListContainer):
+ s.append(f"{name}={list(val)!r}")
+ else:
+ s.append(f"{name}={val!r}")
+ elif dir == "out":
+ s.append(f"{name}=<out>")
+ else:
+ s.append(f"{name}=?")
+
+ return ", ".join(s)
+
+ def print_long_args(self, indent, in_vals, out_vals=None):
+ for i, (name, field) in enumerate(self.args):
+ if name == "ret":
+ continue
+
+ val = self.get_field_val(i, in_vals, out_vals, nullobj=NULL)
+
+ if name in in_vals and out_vals is not None and name not in out_vals:
+ continue
+
+ if self.is_long(val):
+ hdr = f"{indent} {name} = "
+ if isinstance(val, (ListContainer, Container)):
+ print(hdr + str(val).replace("\n", "\n" + indent))
+ elif isinstance(val, bytes):
+ print(hdr + f"({len(val):#x} bytes)")
+ chexdump(val, indent=indent + " ")
+ else:
+ dindent = " " * len(hdr)
+ if isinstance(val, dict) and "_io" in val:
+ del val["_io"]
+ print(hdr + pprint.pformat(val, sort_dicts=False).replace("\n", "\n" + dindent))
+
+ def is_long(self, arg):
+ if isinstance(arg, (list, bytes)):
+ return len(arg) > 4 or any(self.is_long(i) for i in arg)
+
+ return isinstance(arg, (dict, list, bytes))
+
+ def parse_input(self, data):
+ vals = self.in_struct.parse(data)
+
+ return Container({ k: v() if callable(v) else v for k,v in vals.items() })
+
+ def parse_output(self, data, in_vals):
+ context = dict(in_vals)
+
+ if "data" in context:
+ del context["data"]
+
+ vals = self.out_struct.parse(data, **context)
+
+ return Container({ k: v() if callable(v) else v for k,v in vals.items() })
+
+ def __str__(self):
+ if self.rtype is None:
+ rtype = "void"
+ else:
+ rtype = str(self.rtype)
+
+ args = []
+ for name, field in self.args:
+ if name == "ret":
+ continue
+ args.append(f"{field} {name}")
+
+ return f"{rtype} {self.name}({', '.join(args)})"
+
+ def callback(self, func, in_data):
+ in_vals = self.parse_input(in_data)
+
+ args = []
+ kwargs = {}
+
+ out_vals = {}
+
+ for i, (name, field) in enumerate(self.args):
+ if name == "ret":
+ continue
+
+ dir = self.dir[i]
+
+ val = self.get_field_val(i, in_vals, out_vals, nullobj=NULL)
+ is_null = val is NULL
+ if is_null:
+ val = None
+
+ if dir == "inout":
+ if val is not None and not isinstance(val, list):
+ val = ByRef(val)
+ out_vals[name] = val
+ elif dir == "out" and not is_null:
+ val = ByRef(None)
+ out_vals[name] = val
+
+ if self.as_kwargs:
+ kwargs[name] = val
+ else:
+ args.append(val)
+
+ retval = func(*args, **kwargs)
+
+ if self.rtype is None:
+ assert retval is None
+ else:
+ assert retval is not None
+ out_vals["ret"] = retval
+
+ out_vals = {k: v.val if isinstance(v, ByRef) else v for k, v in out_vals.items()}
+
+ context = dict(in_vals)
+
+ if "obj" in context:
+ del context["obj"]
+
+ out_data = self.out_struct.build(out_vals, **context)
+ return out_data
+
+
+ def call(self, call, *args, **kwargs):
+ if args and kwargs:
+ raise Exception("Cannot use both args and kwargs")
+
+ if args:
+ for arg, (name, field) in zip(args, self.args):
+ kwargs[name] = arg
+
+ in_vals = {}
+ out_refs = {}
+
+ for i, (name, field) in enumerate(self.args):
+ if name == "ret":
+ continue
+
+ val = kwargs[name]
+ dir = self.dir[i]
+ nullable = self.nullable[i]
+ array_of_p = self.array_of_p[i]
+
+ if nullable:
+ if not array_of_p:
+ in_vals[name + "_null"] = val is None
+ else:
+ defaults = field.parse(b"\x00" * field.sizeof())
+ in_vals[name + "_null"] = [i is None for i in val]
+ val = [v if v is not None else defaults[i] for i, v in enumerate(val)]
+ else:
+ assert val is not None
+
+ if val is None:
+ continue
+
+ if dir == "out":
+ assert isinstance(val, ByRef)
+ out_refs[name] = val
+ elif dir == "inout":
+ if isinstance(val, ByRef):
+ in_vals[name] = val.val
+ out_refs[name] = val
+ elif val is not None:
+ in_vals[name] = val
+ elif val is not None:
+ in_vals[name] = val
+
+ in_data = self.in_struct.build(in_vals)
+ print(f"{self.name}({self.fmt_args(in_vals)})")
+
+ out_data = call(in_data)
+ out_vals = self.parse_output(out_data, in_vals)
+
+ for k, v in out_refs.items():
+ v.val = out_vals[k]
+
+ if self.rtype is not None:
+ return out_vals["ret"]
+
+def dump_fields(fields):
+ off = 0
+ for f in fields:
+ sizeof = f.sizeof()
+ print(f"{off:#x}: {f} ({sizeof:#x})")
+ off += sizeof
+
+class Call(Method):
+ pass
+
+class Callback(Method):
+ pass
+
+int8_t = Int8sl
+uint8_t = Int8ul
+int16_t = Int16sl
+uint16_t = Int16ul
+int32_t = Int32sl
+uint32_t = Int32ul
+int64_t = Int64sl
+uint64_t = Int64ul
+
+uint = uint32_t
+int_ = int32_t
+ulong = uint64_t
+long_ = int64_t
+
+void = None
+
+class IPCObject:
+ @classmethod
+ def methods(cls):
+ ret = {}
+ for c in cls.mro():
+ ret.update({k: (cls, v) for k, v in cls.__dict__.items() if isinstance(v, Method)})
+
+ return ret
+
+rt_bw_config_t = Struct(
+ "unk1" / UnkBytes(8),
+ "reg1" / Int64ul,
+ "reg2" / Int64ul,
+ "unk2" / UnkBytes(4),
+ "bit" / Int32ul,
+ "padding" / UnkBytes(0x1c),
+)
+
+IOUserClient = Struct(
+ "addr" / Hex(Int64ul),
+ "unk" / Int32ul,
+ "flag1" / Int8ul,
+ "flag2" / Int8ul,
+ Padding(2)
+)
+
+IOMobileFramebufferUserClient = IOUserClient
+
+IOMFBStatus = Int32ul
+IOMFBParameterName = Int32ul
+
+BufferDescriptor = uint64_t
+
+SwapCompleteData = Bytes(0x12)
+SwapInfoBlob = Bytes(0x6c4)
+
+SWAP_SURFACES = 4
+
+Rect = NamedTuple("rect", "x y w h", Int32ul[4])
+
+IOMFBSwapRec = Struct(
+ "ts1" / Default(Int64ul, 0),
+ "ts2" / Default(Int64ul, 0),
+ "unk_10" / Default(Int64ul, 0),
+ "unk_18" / Default(Int64ul, 0),
+ "ts64_unk" / Default(Int64ul, 0),
+ "unk_28" / Default(Int64ul, 0),
+ "ts3" / Default(Int64ul, 0),
+ "unk_38" / Default(Int64ul, 0),
+ "flags1" / Hex(Int64ul),
+ "flags2" / Hex(Int64ul),
+ "swap_id" / Int32ul,
+ "surf_ids" / Int32ul[SWAP_SURFACES],
+ "src_rect" / Rect[SWAP_SURFACES],
+ "surf_flags" / Int32ul[SWAP_SURFACES],
+ "surf_unk" / Int32ul[SWAP_SURFACES],
+ "dst_rect" / Rect[SWAP_SURFACES],
+ "swap_enabled" / Hex(Int32ul),
+ "swap_completed" / Hex(Int32ul),
+ "unk_10c" / Hex(Default(Int32ul, 0)),
+ "unk_110" / UnkBytes(0x1b8),
+ "unk_2c8" / Hex(Default(Int32ul, 0)),
+ "unk_2cc" / UnkBytes(0x14),
+ "unk_2e0" / Hex(Default(Int32ul, 0)),
+ "unk_2e2" / UnkBytes(0x2),
+ "bl_unk" / Hex(Int64ul), # seen: 0x0, 0x1, 0x101, 0x1_0000, 0x101_010101
+ "bl_val" / Hex(Int32ul), # range 0x10000000 - approximately 0x7fe07fc0 for 4 - 510 nits
+ "bl_power" / Hex(Int8ul), # constant 0x40, 0x00: backlight off
+ "unk_2f3" / UnkBytes(0x2d),
+)
+
+assert IOMFBSwapRec.sizeof() == 0x320
+
+MAX_PLANES = 3
+
+ComponentTypes = Struct(
+ "count" / Int8ul,
+ "types" / SizedArray(7, "count", Int8ul),
+)
+
+#ComponentTypes = Bytes(8)
+
+PlaneInfo = Struct(
+ "width" / Int32ul,
+ "height" / Int32ul,
+ "base" / Hex(Int32ul),
+ "offset" / Hex(Int32ul),
+ "stride" / Hex(Int32ul),
+ "size" / Hex(Int32ul),
+ "tile_size" / Int16ul,
+ "tile_w" / Int8ul,
+ "tile_h" / Int8ul,
+ "unk1" / UnkBytes(0xd),
+ "unk2" / Hex(Int8ul),
+ "unk3" / UnkBytes(0x26),
+)
+
+assert PlaneInfo.sizeof() == 0x50
+
+IOSurface = Struct(
+ "is_tiled" / bool_,
+ "unk_1" / bool_,
+ "unk_2" / bool_,
+ "plane_cnt" / Int32ul,
+ "plane_cnt2" / Int32ul,
+ "format" / FourCC,
+ "unk_f" / Default(Hex(Int32ul), 0),
+ "xfer_func" / Int8ul,
+ "colorspace" / Int8ul,
+ "stride" / Int32ul,
+ "pix_size" / Int16ul,
+ "pel_w" / Int8ul,
+ "pel_h" / Int8ul,
+ "offset" / Default(Hex(Int32ul), 0),
+ "width" / Int32ul,
+ "height" / Int32ul,
+ "buf_size" / Hex(Int32ul),
+ "unk_2d" / Default(Int32ul, 0),
+ "unk_31" / Default(Int32ul, 0),
+ "surface_id" / Int32ul,
+ "comp_types" / Default(SizedArray(MAX_PLANES, "plane_cnt", ComponentTypes), []),
+ "has_comp" / Bool(Int64ul),
+ "planes" / Default(SizedArray(MAX_PLANES, "plane_cnt", PlaneInfo), []),
+ "has_planes" / Bool(Int64ul),
+ "compression_info" / Default(SizedArray(MAX_PLANES, "plane_cnt", UnkBytes(0x34)), []),
+ "has_compr_info" / Bool(Int64ul),
+ "unk_1f5" / Int32ul,
+ "unk_1f9" / Int32ul,
+ "padding" / UnkBytes(7),
+)
+
+assert IOSurface.sizeof() == 0x204
+
+IOMFBColorFixedMatrix = Array(5, Array(3, ulong))
+
+class PropID(IntEnum):
+ BrightnessCorrection = 14
+
+class UPPipeAP_H13P(IPCObject):
+ A000 = Call(bool_, "late_init_signal")
+ A029 = Call(void, "setup_video_limits")
+ A034 = Call(void, "update_notify_clients_dcp", Array(14, uint))
+ A035 = Call(bool_, "is_hilo")
+ A036 = Call(bool_, "apt_supported")
+ A037 = Call(uint, "get_dfb_info", InOutPtr(uint), InOutPtr(Array(4, ulong)), InOutPtr(uint))
+ A038 = Call(uint, "get_dfb_compression_info", InOutPtr(uint))
+
+ D000 = Callback(bool_, "did_boot_signal")
+ D001 = Callback(bool_, "did_power_on_signal")
+ D002 = Callback(void, "will_power_off_signal")
+ D003 = Callback(void, "rt_bandwidth_setup_ap", config=OutPtr(rt_bw_config_t))
+
+IdleCachingState = uint32_t
+
+class UnifiedPipeline2(IPCObject):
+ A352 = Call(bool_, "applyProperty", uint, uint)
+ A353 = Call(uint, "get_system_type")
+ A357 = Call(void, "set_create_DFB")
+ A358 = Call(IOMFBStatus, "vi_set_temperature_hint")
+
+ D100 = Callback(void, "match_pmu_service")
+ D101 = Callback(uint32_t, "UNK_get_some_field")
+ D102 = Callback(void, "set_number_property", key=string(0x40), value=uint)
+ D103 = Callback(void, "set_boolean_property", key=string(0x40), value=bool_)
+ D106 = Callback(void, "removeProperty", key=string(0x40))
+ D107 = Callback(bool_, "create_provider_service")
+ D108 = Callback(bool_, "create_product_service")
+ D109 = Callback(bool_, "create_PMU_service")
+ D110 = Callback(bool_, "create_iomfb_service")
+ D111 = Callback(bool_, "create_backlight_service")
+ D112 = Callback(void, "set_idle_caching_state_ap", IdleCachingState, uint)
+ D116 = Callback(bool_, "start_hardware_boot")
+ D117 = Callback(bool_, "is_dark_boot")
+ D118 = Callback(bool_, "is_waking_from_hibernate")
+ D120 = Callback(bool_, "read_edt_data", key=string(0x40), count=uint, value=InOut(Lazy(SizedArray(8, "count", uint32_t))))
+
+ D122 = Callback(bool_, "setDCPAVPropStart", length=uint)
+ D123 = Callback(bool_, "setDCPAVPropChunk", data=HexDump(SizedBytes(0x1000, "length")), offset=uint, length=uint)
+ D124 = Callback(bool_, "setDCPAVPropEnd", key=string(0x40))
+
+class UPPipe2(IPCObject):
+ A102 = Call(uint64_t, "test_control", cmd=uint64_t, arg=uint)
+ A103 = Call(void, "get_config_frame_size", width=InOutPtr(uint), height=InOutPtr(uint))
+ A104 = Call(void, "set_config_frame_size", width=uint, height=uint)
+ A105 = Call(void, "program_config_frame_size")
+ A130 = Call(bool_, "init_ca_pmu")
+ A131 = Call(bool_, "pmu_service_matched")
+ A132 = Call(bool_, "backlight_service_matched")
+
+ D201 = Callback(uint32_t, "map_buf", buf=InPtr(BufferDescriptor), vaddr=OutPtr(ulong), dva=OutPtr(ulong), unk=bool_)
+ D202 = Callback(void, "unmap_buf", buf=InPtr(BufferDescriptor), unk1=uint, unk2=ulong, unkB=uint)
+
+ D206 = Callback(bool_, "match_pmu_service_2")
+ D207 = Callback(bool_, "match_backlight_service")
+ D208 = Callback(uint64_t, "get_calendar_time_ms")
+ D211 = Callback(void, "update_backlight_factor_prop", int_)
+
+class PropRelay(IPCObject):
+ D300 = Callback(void, "pr_publish", prop_id=uint32_t, value=int_)
+
+class IOMobileFramebufferAP(IPCObject):
+ A401 = Call(uint32_t, "start_signal")
+
+ A407 = Call(uint32_t, "swap_start", swap_id=InOutPtr(uint), client=InOutPtr(IOUserClient))
+ A408 = Call(uint32_t, "swap_submit_dcp",
+ swap_rec=InPtr(IOMFBSwapRec),
+ surfaces=Array(4, InPtr(IOSurface)),
+ surfAddr=Array(4, Hex(ulong)),
+ unkBool=bool_,
+ unkFloat=Float64l,
+ unkInt=uint,
+ unkOutBool=OutPtr(bool_))
+
+ A410 = Call(uint32_t, "set_display_device", uint)
+ A411 = Call(bool_, "is_main_display")
+ A438 = Call(uint32_t, "swap_set_color_matrix", matrix=InOutPtr(IOMFBColorFixedMatrix), func=uint32_t, unk=uint)
+#"A438": "IOMobileFramebufferAP::swap_set_color_matrix(IOMFBColorFixedMatrix*, IOMFBColorMatrixFunction, unsigned int)",
+
+ A412 = Call(uint32_t, "set_digital_out_mode", uint, uint)
+ A413 = Call(uint32_t, "get_digital_out_state", InOutPtr(uint))
+ A414 = Call(uint32_t, "get_display_area", InOutPtr(ulong))
+ A419 = Call(uint32_t, "get_gamma_table", InOutPtr(Bytes(0xc0c)))
+ A422 = Call(uint32_t, "set_matrix", uint, InPtr(Array(3, Array(3, ulong))))
+ A423 = Call(uint32_t, "set_contrast", InOutPtr(Float32l))
+ A426 = Call(uint32_t, "get_color_remap_mode", InOutPtr(uint32_t))
+ A427 = Call(uint32_t, "setBrightnessCorrection", uint)
+
+ A435 = Call(uint32_t, "set_block_dcp", arg1=uint64_t, arg2=uint, arg3=uint, arg4=Array(8, ulong), arg5=uint, data=SizedBytes(0x1000, "length"), length=ulong)
+ A439 = Call(uint32_t, "set_parameter_dcp", param=IOMFBParameterName, value=Lazy(SizedArray(4, "count", ulong)), count=uint)
+
+ A440 = Call(uint, "display_width")
+ A441 = Call(uint, "display_height")
+ A442 = Call(void, "get_display_size", OutPtr(uint), OutPtr(uint))
+ A443 = Call(int_, "do_create_default_frame_buffer")
+ A444 = Call(void, "printRegs")
+ A447 = Call(int_, "enable_disable_video_power_savings", uint)
+ A454 = Call(void, "first_client_open")
+ A455 = Call(void, "last_client_close_dcp", OutPtr(uint))
+ A456 = Call(bool_, "writeDebugInfo", ulong)
+ A457 = Call(void, "flush_debug_flags", uint)
+ A458 = Call(bool_, "io_fence_notify", uint, uint, ulong, IOMFBStatus)
+ A460 = Call(bool_, "setDisplayRefreshProperties")
+ A463 = Call(void, "flush_supportsPower", bool_)
+ A464 = Call(uint, "abort_swaps_dcp", InOutPtr(IOMobileFramebufferUserClient))
+
+ A467 = Call(uint, "update_dfb", surf=InPtr(IOSurface))
+ A468 = Call(uint32_t, "setPowerState", ulong, bool_, OutPtr(uint))
+ A469 = Call(bool_, "isKeepOnScreen")
+
+ D552 = Callback(bool_, "setProperty_dict", key=string(0x40), value=InPtr(Padded(0x1000, OSDictionary())))
+ D561 = Callback(bool_, "setProperty_dict", key=string(0x40), value=InPtr(Padded(0x1000, OSDictionary())))
+ D563 = Callback(bool_, "setProperty_int", key=string(0x40), value=InPtr(uint64_t))
+ D565 = Callback(bool_, "setProperty_bool", key=string(0x40), value=InPtr(Bool(uint32_t)))
+ D567 = Callback(bool_, "setProperty_str", key=string(0x40), value=string(0x40))
+
+ D574 = Callback(IOMFBStatus, "powerUpDART", bool_)
+
+ D575 = Callback(bool_, "get_dot_pitch", OutPtr(uint))
+ D576 = Callback(void, "hotPlug_notify_gated", ulong)
+ D577 = Callback(void, "powerstate_notify", bool_, bool_)
+ D578 = Callback(bool_, "idle_fence_create", IdleCachingState)
+ D579 = Callback(void, "idle_fence_complete")
+
+ D581 = Callback(void, "swap_complete_head_of_line", uint, bool_, uint, bool_)
+ D582 = Callback(bool_, "create_default_fb_surface", uint, uint)
+ D583 = Callback(bool_, "serializeDebugInfoCb", ulong, InPtr(uint64_t), uint)
+ D584 = Callback(void, "clear_default_surface")
+
+ D588 = Callback(void, "resize_default_fb_surface_gated")
+ D589 = Callback(void, "swap_complete_ap_gated", swap_id=uint, unkBool=bool_, swap_data=InPtr(SwapCompleteData), swap_info=SwapInfoBlob, unkUint=uint)
+
+ D591 = Callback(void, "swap_complete_intent_gated", swap_id=uint, unkB=bool_, unkInt=uint32_t, width=uint, height=uint)
+ D593 = Callback(void, "enable_backlight_message_ap_gated", bool_)
+ D594 = Callback(void, "setSystemConsoleMode", bool_)
+
+ D596 = Callback(bool_, "isDFBAllocated")
+ D597 = Callback(bool_, "preserveContents")
+ D598 = Callback(void, "find_swap_function_gated")
+
+class ServiceRelay(IPCObject):
+ D400 = Callback(void, "get_property", obj=FourCC, key=string(0x40), value=OutPtr(Bytes(0x200)), lenght=InOutPtr(uint))
+ D401 = Callback(bool_, "sr_get_uint_prop", obj=FourCC, key=string(0x40), value=InOutPtr(ulong))
+ D404 = Callback(void, "sr_set_uint_prop", obj=FourCC, key=string(0x40), value=uint)
+ D406 = Callback(void, "set_fx_prop", obj=FourCC, key=string(0x40), value=uint)
+ D408 = Callback(uint64_t, "sr_getClockFrequency", obj=FourCC, arg=uint)
+ D411 = Callback(IOMFBStatus, "sr_mapDeviceMemoryWithIndex", obj=FourCC, index=uint, flags=uint, addr=OutPtr(ulong), length=OutPtr(ulong))
+ D413 = Callback(bool_, "sr_setProperty_dict", obj=FourCC, key=string(0x40), value=InPtr(Padded(0x1000, OSDictionary())))
+ D414 = Callback(bool_, "sr_setProperty_int", obj=FourCC, key=string(0x40), value=InPtr(uint64_t))
+ D415 = Callback(bool_, "sr_setProperty_bool", obj=FourCC, key=string(0x40), value=InPtr(Bool(uint32_t)))
+
+mem_desc_id = uint
+
+class MemDescRelay(IPCObject):
+ D451 = Callback(mem_desc_id, "allocate_buffer", uint, ulong, uint, OutPtr(ulong), OutPtr(ulong), OutPtr(ulong))
+ D452 = Callback(mem_desc_id, "map_physical", paddr=ulong, size=ulong, flags=uint, dva=OutPtr(ulong), dvasize=OutPtr(ulong))
+ D453 = Callback(mem_desc_id, "withAddressRange", ulong, ulong, uint, uint64_t, OutPtr(uint), OutPtr(ulong))
+ D454 = Callback(IOMFBStatus, "prepare", uint, uint)
+ D455 = Callback(IOMFBStatus, "complete", uint, uint)
+ D456 = Callback(bool_, "release_descriptor", uint)
+
+ALL_CLASSES = [
+ UPPipeAP_H13P,
+ UnifiedPipeline2,
+ IOMobileFramebufferAP,
+ ServiceRelay,
+ PropRelay,
+ UPPipe2,
+ MemDescRelay,
+]
+
+ALL_METHODS = {}
+
+for cls in ALL_CLASSES:
+ ALL_METHODS.update(cls.methods())
+
+SHORT_CHANNELS = {
+ "CB": "d",
+ "CMD": "C",
+ "ASYNC": "a",
+ "OOBCMD": "O",
+ "OOBCB": "o",
+}
+
+RDIR = { ">": "<", "<": ">" }
+
+class Call:
+ def __init__(self, dir, chan, off, msg, in_size, out_size, in_data=b''):
+ self.dir = dir
+ self.chan = chan
+ self.msg = msg
+ self.off = off
+ self.in_size = in_size
+ self.out_size = out_size
+ self.in_data = in_data
+ self.out_data = None
+ self.complete = False
+ self.ret = None
+
+ def ack(self, out_data):
+ self.out_data = out_data
+ self.complete = True
+
+ def print_req(self, indent=""):
+ log = f"{indent}{self.dir}{SHORT_CHANNELS[self.chan]}[{self.off:#x}] {self.msg} "
+
+ cls, method = ALL_METHODS.get(self.msg, (None, None))
+ if cls is None:
+ print(log + f"{self.in_size:#x}/{self.out_size:#x}")
+ return
+
+ log += f"{cls.__name__}::{method.name}("
+ in_size = method.in_struct.sizeof()
+
+ if in_size != len(self.in_data):
+ print(f"{log} !! Expected {in_size:#x} bytes, got {len(self.in_data):#x} bytes (in)")
+ dump_fields(method.in_fields)
+ chexdump(self.in_data)
+ self.in_vals = {}
+ return
+
+ self.in_vals = method.parse_input(self.in_data)
+
+ log += f"{method.fmt_args(self.in_vals)})"
+
+ print(log)
+
+ method.print_long_args(indent, self.in_vals)
+ #if method.in_fields:
+ #print(self.in_vals)
+
+ def print_reply(self, indent=""):
+ assert self.complete
+ log = f"{indent}{RDIR[self.dir]}{SHORT_CHANNELS[self.chan]}[{self.off:#x}] {self.msg} "
+
+ cls, method = ALL_METHODS.get(self.msg, (None, None))
+ if cls is None:
+ print(log + f"{self.in_size:#x}/{self.out_size:#x}")
+ return
+
+ log += f"{cls.__name__}::{method.name}("
+ out_size = method.out_struct.sizeof()
+
+ if out_size != len(self.out_data):
+ print(f"{log} !! Expected {out_size:#x} bytes, got {len(self.out_data):#x} bytes (out)")
+ dump_fields(method.out_fields)
+ chexdump(self.out_data)
+ return
+
+ self.out_vals = method.parse_output(self.out_data, self.in_vals)
+
+ log += f"{method.fmt_args(self.in_vals, self.out_vals)})"
+
+ if "ret" in self.out_vals:
+ self.ret = self.out_vals.ret
+ del self.out_vals["ret"]
+ log += f" = {self.ret!r}"
+
+ print(log)
+
+ method.print_long_args(indent, self.in_vals, self.out_vals)
+ #if len(method.out_fields) - (self.ret is not None):
+ #print(self.out_vals)
diff --git a/tools/proxyclient/m1n1/fw/dcp/manager.py b/tools/proxyclient/m1n1/fw/dcp/manager.py
new file mode 100644
index 0000000..7977c3a
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/dcp/manager.py
@@ -0,0 +1,319 @@
+# SPDX-License-Identifier: MIT
+import pprint
+import struct, functools, time
+from dataclasses import dataclass
+from enum import IntEnum
+
+from construct.lib import hexundump
+
+from ..asc.base import *
+from ...utils import *
+
+from . import ipc
+from .dcpep import CallContext
+
+## DCP API manager
+
+class DCPBaseManager:
+ def __init__(self, dcpep):
+ self.dcpep = dcpep
+ self.dcp = dcpep.asc
+ dcpep.mgr = self
+
+ self.name_map = {}
+ self.tag_map = {}
+
+ self.in_callback = 0
+
+ for k, (cls, v) in ipc.ALL_METHODS.items():
+ self.name_map[v.name] = k, v
+ self.tag_map[k] = v
+
+ def handle_cb(self, state):
+ method = self.tag_map.get(state.tag, None)
+ if method is None:
+ raise Exception(f"Unknown callback {state.tag}")
+
+ func = getattr(self, method.name, None)
+
+ if func is None:
+ raise Exception(f"Unimplemented callback {method!s} [{state.tag}]")
+
+ self.in_callback += 1
+ try:
+ retval = method.callback(func, state.in_data)
+ except Exception as e:
+ print(f"Exception in callback {method.name}")
+ raise
+ self.in_callback -= 1
+ return retval
+
+ def __getattr__(self, attr):
+ tag, method = self.name_map.get(attr, (None, None))
+ if method is None or tag.startswith("D"):
+ raise AttributeError(f"Unknown method {attr}")
+
+ out_len = method.out_struct.sizeof()
+ if self.in_callback:
+ ctx = CallContext.CB
+ else:
+ ctx = CallContext.CMD
+ rpc = functools.partial(self.dcpep.ch_cmd.call, ctx, tag, out_len=out_len)
+ return functools.partial(method.call, rpc)
+
+class DCPManager(DCPBaseManager):
+ def __init__(self, dcpep, compatible='t8103'):
+ super().__init__(dcpep)
+
+ self.iomfb_prop = {}
+ self.dcpav_prop = {}
+ self.service_prop = {}
+ self.pr_prop = {}
+
+ self.swaps = 0
+ self.frame = 0
+
+ self.mapid = 0
+ self.bufs = {}
+
+ self.compatible = compatible
+
+ ## IOMobileFramebufferAP methods
+
+ def find_swap_function_gated(self):
+ pass
+
+ def create_provider_service(self):
+ return True
+
+ def create_product_service(self):
+ return True
+
+ def create_PMU_service(self):
+ return True
+
+ def create_iomfb_service(self):
+ return True
+
+ def create_backlight_service(self):
+ return False
+
+ def setProperty(self, key, value):
+ self.iomfb_prop[key] = value
+ print(f"setProperty({key} = {value!r})")
+ return True
+
+ setProperty_dict = setProperty_int = setProperty_bool = setProperty_str = setProperty
+
+ def swap_complete_ap_gated(self, swap_id, unkBool, swap_data, swap_info, unkUint):
+ swap_data_ptr = "NULL" if swap_data is None else "..."
+ print(f"swap_complete_ap_gated({swap_id}, {unkBool}, {swap_data_ptr}, ..., {unkUint}")
+ if swap_data is not None:
+ chexdump(swap_data)
+ chexdump(swap_info)
+ self.swaps += 1
+ self.frame = swap_id
+
+ def swap_complete_intent_gated(self, swap_id, unkB, unkInt, width, height):
+ print(f"swap_complete_intent_gated({swap_id}, {unkB}, {unkInt}, {width}, {height}")
+ self.swaps += 1
+ self.frame = swap_id
+
+ def enable_backlight_message_ap_gated(self, unkB):
+ print(f"enable_backlight_message_ap_gated({unkB})")
+
+ # wrapper for set_digital_out_mode to print information on the setted modes
+ def SetDigitalOutMode(self, color_id, timing_id):
+ color_mode = [x for x in self.dcpav_prop['ColorElements'] if x['ID'] == color_id][0]
+ timing_mode = [x for x in self.dcpav_prop['TimingElements'] if x['ID'] == timing_id][0]
+ pprint.pprint(color_mode)
+ pprint.pprint(timing_mode)
+ self.set_digital_out_mode(color_id, timing_id)
+
+ ## UPPipeAP_H13P methods
+
+ def did_boot_signal(self):
+ return True
+
+ def did_power_on_signal(self):
+ return True
+
+ def will_power_off_signal(self):
+ return
+
+ def rt_bandwidth_setup_ap(self, config):
+ print("rt_bandwidth_setup_ap(...)")
+ if self.compatible == 't8103':
+ config.val = {
+ "reg1": 0x23b738014, # reg[5] in disp0/dispext0, plus 0x14 - part of pmgr
+ "reg2": 0x23bc3c000, # reg[6] in disp0/dispext0 - part of pmp/pmgr
+ "bit": 2,
+ }
+ elif self.compatible == 't600x':
+ config.val = {
+ "reg1": 0x28e3d0000 + 0x988, # reg[4] in disp0/dispext0, plus 0x988
+ "reg2": 0x0,
+ "bit": 0,
+ }
+ else:
+ raise ValueError(self.compatible)
+
+ ## UnifiedPipeline2 methods
+
+ def match_pmu_service(self):
+ pass
+
+ def set_number_property(self, key, value):
+ pass
+
+ def create_provider_service(self):
+ return True
+
+ def is_dark_boot(self):
+ return False
+
+ def read_edt_data(self, key, count, value):
+ return False
+
+ def UNK_get_some_field(self):
+ return 0
+
+ def start_hardware_boot(self):
+ self.set_create_DFB()
+ self.do_create_default_frame_buffer()
+ self.setup_video_limits()
+ self.flush_supportsPower(True)
+ self.late_init_signal()
+ self.setDisplayRefreshProperties()
+ return True
+
+ def setDCPAVPropStart(self, length):
+ print(f"setDCPAVPropStart({length:#x})")
+ self.dcpav_prop_len = length - 1 # off by one?
+ self.dcpav_prop_off = 0
+ self.dcpav_prop_data = []
+ return True
+
+ def setDCPAVPropChunk(self, data, offset, length):
+ print(f"setDCPAVPropChunk(..., {offset:#x}, {length:#x})")
+ assert offset == self.dcpav_prop_off
+ self.dcpav_prop_data.append(data)
+ self.dcpav_prop_off += len(data)
+ return True
+
+ def setDCPAVPropEnd(self, key):
+ print(f"setDCPAVPropEnd({key!r})")
+ blob = b"".join(self.dcpav_prop_data)
+ assert self.dcpav_prop_len == len(blob)
+ self.dcpav_prop[key] = ipc.OSSerialize().parse(blob)
+ self.dcpav_prop_data = self.dcpav_prop_len = self.dcpav_prop_off = None
+ #pprint.pprint(self.dcpav_prop[key])
+ return True
+
+ def set_boolean_property(self, key, value):
+ print(f"set {key!r} = {value}")
+
+ def removeProperty(self, key):
+ print(f"removeProperty({key!r})")
+
+ def powerstate_notify(self, unk1, unk2):
+ print(f"powerstate_notify({unk1}, {unk2})")
+
+ def create_default_fb_surface(self, width, height):
+ print(f"create_default_fb_surface({width}x{height})")
+ return True
+
+ def powerUpDART(self, unk):
+ print(f"powerUpDART({unk})")
+ return 0
+
+ def hotPlug_notify_gated(self, unk):
+ print(f"hotPlug_notify_gated({unk})")
+
+ def is_waking_from_hibernate(self):
+ return False
+
+ ## UPPipe2 methods
+
+ def match_pmu_service_2(self):
+ return True
+
+ def match_backlight_service(self):
+ return True
+
+ def get_calendar_time_ms(self):
+ return time.time_ns() // 1000_000
+
+ def update_backlight_factor_prop(self, value):
+ pass
+
+ def map_buf(self, buf, vaddr, dva, unk):
+ print(f"map buf {buf}, {unk}")
+ paddr, dcpdva, dvasize = self.bufs[buf]
+ vaddr.val = 0
+ dva.val = self.dcp.disp_dart.iomap(4, paddr, dvasize)
+ print(f"mapped to dva {dva}")
+ return 0
+
+ def update_backlight_factor_prop(self, unk):
+ print(f"update_backlight_factor_prop {unk}")
+
+ ## ServiceRelay methods
+
+ def sr_setProperty(self, obj, key, value):
+ self.service_prop.setdefault(obj, {})[key] = value
+ print(f"sr_setProperty({obj}/{key} = {value!r})")
+ return True
+
+ def sr_getClockFrequency(self, obj, arg):
+ print(f"sr_getClockFrequency({obj}, {arg})")
+ return 533333328
+
+ sr_setProperty_dict = sr_setProperty_int = sr_setProperty_bool = sr_setProperty_str = sr_setProperty
+
+ def sr_get_uint_prop(self, obj, key, value):
+ value.val = 0
+ return False
+
+ def sr_set_uint_prop(self, obj, key, value):
+ print(f"sr_set_uint_prop({obj}, {key} = {value})")
+
+ def set_fx_prop(self, obj, key, value):
+ print(f"set_fx_prop({obj}, {key} = {value})")
+
+ def sr_mapDeviceMemoryWithIndex(self, obj, index, flags, addr, length):
+ assert obj == "PROV"
+ addr.val, length.val = self.dcp.u.adt["/arm-io/disp0"].get_reg(index)
+ print(f"sr_mapDeviceMemoryWithIndex({obj}, {index}, {flags}, {addr.val:#x}, {length.val:#x})")
+ return 0
+
+ ## PropRelay methods
+
+ def pr_publish(self, prop_id, value):
+ self.pr_prop[prop_id] = value
+ print(f"pr_publish({prop_id}, {value!r})")
+
+ ## MemDescRelay methods:
+
+ def allocate_buffer(self, unk0, size, unk1, paddr, dva, dvasize):
+ print(f"allocate_buffer({unk0}, {size}, {unk1})")
+
+ dvasize.val = align_up(size, 4096)
+ paddr.val = self.dcp.u.memalign(0x4000, size)
+ dva.val = self.dcp.dart.iomap(0, paddr.val, size)
+
+ self.mapid += 1
+ print(f"Allocating {self.mapid} as {hex(paddr.val)} / {hex(dva.val)}")
+
+ self.bufs[self.mapid] = (paddr.val, dva.val, dvasize.val)
+
+ return self.mapid
+
+ def map_physical(self, paddr, size, flags, dva, dvasize):
+ dvasize.val = align_up(size, 4096)
+ dva.val = self.dcp.dart.iomap(0, paddr, size)
+ print(f"map_physical({paddr:#x}, {size:#x}, {flags}, {dva.val:#x}, {dvasize.val:#x})")
+
+ self.mapid += 1
+ return self.mapid
+
diff --git a/tools/proxyclient/m1n1/fw/dcp/parse_log.py b/tools/proxyclient/m1n1/fw/dcp/parse_log.py
new file mode 100644
index 0000000..e57f637
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/dcp/parse_log.py
@@ -0,0 +1,43 @@
+# SPDX-License-Identifier: MIT
+
+from m1n1.utils import *
+from m1n1.fw.dcp.ipc import *
+
+def parse_log(fd):
+ op_stack = {}
+ for line in fd:
+ optype, args = line.split(" ", 1)
+ if optype == "CALL":
+ d, msg, chan, off, msg, in_size, out_size, in_data = args.split(" ")
+ op = Call(d, chan, int(off, 0), msg, int(in_size, 0), int(out_size, 0),
+ bytes.fromhex(in_data))
+ op_stack.setdefault(chan, []).append(op)
+ elif optype == "ACK":
+ d, msg, chan, off, out_data = args.split(" ")
+ op = op_stack[chan].pop()
+ assert int(off, 0) == op.off
+ op.ack(bytes.fromhex(out_data))
+ else:
+ raise Exception(f"Unknown log cmd {optype}")
+
+ yield op
+
+def dump_log(fd):
+ nesting = {
+ "": 0,
+ "OOB": 0,
+ }
+ for op in parse_log(fd):
+ ctx = ""
+ if "OOB" in op.chan:
+ ctx = "[OOB] -----------> "
+ if not op.complete:
+ op.print_req(indent=ctx + " " * nesting.setdefault(ctx, 0))
+ nesting[ctx] += 1
+ else:
+ nesting[ctx] -= 1
+ op.print_reply(indent=ctx + " " * nesting.setdefault(ctx, 0))
+
+if __name__ == "__main__":
+ import sys
+ dump_log(open(sys.argv[1]))
diff --git a/tools/proxyclient/m1n1/fw/mtp.py b/tools/proxyclient/m1n1/fw/mtp.py
new file mode 100644
index 0000000..5b22b8a
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/mtp.py
@@ -0,0 +1,411 @@
+# SPDX-License-Identifier: MIT
+
+import struct
+from construct import *
+from ..constructutils import *
+from ..utils import *
+
+class HIDDescriptor(ConstructClass):
+ subcon = Struct(
+ "descriptor" / HexDump(GreedyBytes)
+ )
+
+class GPIOInit(ConstructClass):
+ subcon = Struct(
+ "unk1" / Int16ul,
+ "gpio_id"/ Int16ul,
+ "gpio_name" / PaddedString(32, "ascii")
+ )
+
+class InitBlock(ConstructClass):
+ subcon = Struct(
+ "type" / Int16ul,
+ "subtype" / Int16ul,
+ "length" / Int16ul,
+ "payload" / FixedSized(this.length,
+ Switch(this.type, {
+ 0: HIDDescriptor,
+ 1: GPIOInit,
+ 2: Bytes(0),
+ }, default=GreedyBytes))
+ )
+
+class InitMsg(ConstructClass):
+ subcon = Struct(
+ "msg_type" / Const(0xf0, Int8ul),
+ "msg_subtype" / Const(0x01, Int8ul),
+ "unk" / Const(0x00, Int8ul),
+ "device_id" / Int8ul,
+ "device_name" / PaddedString(16, "ascii"),
+ "msg" / RepeatUntil(lambda obj, lst, ctx: lst[-1].type == 2, InitBlock)
+ )
+
+class DeviceReadyMsg(ConstructClass):
+ subcon = Struct(
+ "msg_type" / Const(0xf1, Int8ul),
+ "device_id" / Int8ul,
+ "unk" / Int16ul
+ )
+
+class GPIORequestMsg(ConstructClass):
+ subcon = Struct(
+ "msg_type" / Const(0xa0, Int8ul),
+ "device_id" / Int8ul,
+ "gpio_num" / Int8ul,
+ "cmd" / Int16ul,
+ "args" / HexDump(GreedyBytes)
+ )
+
+NotificationMsg = Select(
+ DeviceReadyMsg,
+ InitMsg,
+ GPIORequestMsg,
+ HexDump(GreedyBytes),
+)
+
+class UnkDeviceControlMsg(ConstructClass):
+ subcon = Struct(
+ "command" / Int8ul,
+ "args" / HexDump(GreedyBytes),
+ )
+
+class DeviceEnableMsg(ConstructClass):
+ subcon = Struct(
+ "command" / Const(0xb4, Int8ul),
+ "device_id" / Int8ul,
+ )
+
+class DeviceResetMsg(ConstructClass):
+ subcon = Struct(
+ "command" / Const(0x40, Int8ul),
+ "unk1" / Int8ul,
+ "device_id" / Int8ul,
+ "state" / Int8ul,
+ )
+
+class InitBufMsg(ConstructClass):
+ subcon = Struct(
+ "command" / Const(0x91, Int8ul),
+ "unk1" / Int8ul,
+ "unk2" / Int8ul,
+ "buf_addr" / Int64ul,
+ "buf_size" / Int32ul,
+ )
+
+class InitAFEMsg(ConstructClass):
+ subcon = Struct(
+ "command" / Const(0x95, Int8ul),
+ "unk1" / Int8ul,
+ "unk2" / Int8ul,
+ "iface" / Int8ul,
+ "buf_addr" / Int64ul,
+ "buf_size" / Int32ul,
+ )
+
+class UnkMsgC1(ConstructClass):
+ subcon = Struct(
+ "command" / Const(0xc1, Int8ul),
+ "unk1" / Int8ul,
+ )
+
+class GPIOAckMsg(ConstructClass):
+ subcon = Struct(
+ "command" / Const(0xa1, Int8ul),
+ "unk" / Int32ul,
+ "msg" / GPIORequestMsg,
+ )
+
+DeviceControlMsg = Select(
+ DeviceEnableMsg,
+ DeviceResetMsg,
+ InitAFEMsg,
+ InitBufMsg,
+ UnkMsgC1,
+ UnkDeviceControlMsg
+)
+
+class DeviceControlAck(ConstructClass):
+ subcon = Struct(
+ "command" / Int8ul
+ )
+
+class MessageHeader(ConstructClass):
+ subcon = Struct(
+ "flags" / Int16ul,
+ "length" / Int16ul,
+ "retcode" / Int32ul,
+ )
+
+class TXMessage(ConstructClass):
+ subcon = Struct(
+ "hdr" / MessageHeader,
+ "msg" / FixedSized(this.hdr.length,
+ Switch(this.hdr.flags, {
+ 0x40: HexDump(GreedyBytes),
+ 0x80: DeviceControlMsg,
+ 0x81: Int8ul,
+ }))
+ )
+
+ def __init__(self):
+ self.hdr = MessageHeader()
+
+class RXMessage(ConstructClass):
+ subcon = Struct(
+ "hdr" / MessageHeader,
+ "msg" / FixedSized(this.hdr.length, HexDump(GreedyBytes)),
+ )
+
+class MTPInterface:
+ def __init__(self, proto, iface):
+ self.proto = proto
+ self.iface = iface
+ self.tx_seq = 0
+ self.initialized = False
+ self.gpios = {}
+
+ def send(self, msg):
+ self.proto.send(self.iface, self.tx_seq & 0xff, msg)
+ self.tx_seq += 1
+
+ def get_report(self, idx):
+ msg = TXMessage()
+ msg.hdr.flags = 0x81
+ msg.hdr.length = 1
+ msg.hdr.retcode = 0
+ msg.msg = idx
+ self.send(msg.build())
+
+ def packet(self, pkt):
+ self.log(f"RX: {pkt.hex()}")
+
+ def log(self, s):
+ self.proto.log(f"[{self.NAME}] " + s)
+
+ def initialize(self):
+ self.proto.comm.enable_device(self.iface)
+
+ def report(self, msg):
+ self.log(f"report: {msg.hex()}")
+
+ def ack(self, msg):
+ self.log(f"ack: {msg.hex()}")
+
+ def unk(self, msg):
+ self.log(f"unk: {msg.hex()}")
+
+ def packet(self, pkt):
+ msg = RXMessage.parse(pkt)
+ mtype = msg.hdr.flags
+ #self.log(f"FL:{msg.hdr.flag s:04x} unk:{msg.hdr.unk:08x}")
+ if mtype == 0x00:
+ self.report(msg.msg)
+ elif mtype == 0x80:
+ self.ack(msg.hdr.retcode, msg.msg)
+ elif mtype == 0x81:
+ self.log(f"REPORT")
+ chexdump(msg.msg, print_fn=self.log)
+ elif mtype == 0x40:
+ self.unk(msg.msg)
+
+ def __str__(self):
+ return f"{self.iface}/{self.NAME}"
+
+
+class MTPCommInterface(MTPInterface):
+ NAME = "comm"
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.last_cmd = None
+ self.gpios = {}
+
+
+ def device_control(self, dcmsg):
+ while self.last_cmd is not None:
+ self.proto.work()
+ msg = TXMessage()
+ msg.hdr.flags = 0x80
+ msg.hdr.length = len(dcmsg.build())
+ msg.hdr.retcode = 0
+ msg.msg = dcmsg
+ #self.log(f"Send device control {dcmsg}")
+ self.last_cmd = dcmsg.command
+ self.send(msg.build())
+ while self.last_cmd is not None:
+ self.proto.work()
+
+ def enable_device(self, iface):
+ msg = DeviceEnableMsg()
+ msg.device_id = iface
+ self.device_control(msg)
+
+ def report(self, msg):
+ msg = NotificationMsg.parse(msg)
+
+ if isinstance(msg, DeviceReadyMsg):
+ iface = self.proto.iface[msg.device_id]
+ iface.initialized = True
+ self.log(f"{iface}: init complete")
+ elif isinstance(msg, InitMsg):
+ iface = self.proto.get_interface(msg.device_id, msg.device_name)
+ for blk in msg.msg:
+ if isinstance(blk.payload, HIDDescriptor):
+ self.log(f"Got HID descriptor for {iface}:")
+ iface.descriptor = blk.payload.descriptor
+ self.log(hexdump(iface.descriptor))
+ iface.initialize()
+ elif isinstance(blk.payload, GPIOInit):
+ self.log(f"GPIO Init: {blk.payload}")
+ prop = getattr(self.proto.node[msg.device_name],
+ f"function-{blk.payload.gpio_name}".replace("-", "_"))
+ key = struct.pack(">I", prop.args[0]).decode("ascii")
+ val = prop.args[1]
+ self.log(f"GPIO key: {key}")
+ self.gpios[(msg.device_id, blk.payload.gpio_id)] = key, val
+ elif isinstance(msg, GPIORequestMsg):
+ self.log(f"GPIO request: {msg}")
+ smcep = self.proto.smc.epmap[0x20]
+ key, val = self.gpios[(msg.device_id, msg.gpio_num)]
+ if msg.cmd == 3:
+ smcep.write32(key, val | 1)
+ smcep.write32(key, val)
+
+ ackmsg = GPIOAckMsg()
+ ackmsg.unk = 0
+ ackmsg.msg = msg
+ self.device_control(ackmsg)
+
+ def ack(self, retcode, msg):
+ msg = DeviceControlAck.parse(msg)
+ self.log(f"Got ACK for {msg.command:#x}: {retcode:08x}")
+ assert msg.command == self.last_cmd
+ self.last_cmd = None
+
+ def init_afe(self, iface, data):
+ paddr, dva = self.proto.mtp.ioalloc(len(data))
+ self.proto.u.iface.writemem(paddr, data)
+
+ afemsg = InitAFEMsg()
+ afemsg.unk1 = 2
+ afemsg.unk2 = 0
+ afemsg.iface = iface
+ afemsg.buf_addr = dva
+ afemsg.buf_size = len(data)
+ self.device_control(afemsg)
+
+ def device_reset(self, iface, unk1, state):
+ self.log(f"device_reset({iface}, {unk1}, {state})")
+ rmsg = DeviceResetMsg()
+ rmsg.device_id = iface
+ rmsg.unk1 = unk1
+ rmsg.state = state
+ self.device_control(rmsg)
+
+class MTPHIDInterface(MTPInterface):
+ pass
+
+class MTPMultitouchInterface(MTPHIDInterface):
+ NAME = "multi-touch"
+
+ def initialize(self):
+ super().initialize()
+
+ #data = open("afe.bin", "rb").read()
+ #self.proto.comm.init_afe(self.iface, data)
+ #self.proto.comm.device_reset(self.iface, 1, 0)
+ #self.proto.comm.device_reset(self.iface, 1, 2)
+
+class MTPKeyboardInterface(MTPHIDInterface):
+ NAME = "keyboard"
+
+class MTPSTMInterface(MTPHIDInterface):
+ NAME = "stm"
+
+class MTPActuatorInterface(MTPHIDInterface):
+ NAME = "actuator"
+
+class MTPTPAccelInterface(MTPHIDInterface):
+ NAME = "tp_accel"
+
+class MTPProtocol:
+ INTERFACES = [
+ MTPCommInterface,
+ MTPMultitouchInterface,
+ MTPKeyboardInterface,
+ MTPSTMInterface,
+ MTPActuatorInterface,
+ MTPTPAccelInterface,
+ ]
+
+ def __init__(self, u, node, mtp, dockchannel, smc):
+ self.node = node
+ self.smc = smc
+ self.u = u
+ self.mtp = mtp
+ self.dockchannel = dockchannel
+ self.iface = {}
+
+ # Add initial comm interface
+ self.get_interface(0, "comm")
+
+ def get_interface(self, iface, name):
+ if iface in self.iface:
+ return self.iface[iface]
+
+ for cls in self.INTERFACES:
+ if cls.NAME == name:
+ break
+ else:
+ self.log(f"Unknown interface name {name}")
+ return None
+ obj = cls(self, iface)
+ self.iface[iface] = obj
+ setattr(self, name.replace("-", "_"), obj)
+ return obj
+
+ def checksum(self, d):
+ assert len(d) % 4 == 0
+ c = len(d) // 4
+ return 0xffffffff - sum(struct.unpack(f"<{c}I", d)) & 0xffffffff
+
+ def read_pkt(self):
+ self.mtp.work_pending()
+ hdr = self.dockchannel.read(8)
+ hlen, mtype, size, ctr, devid, pad = struct.unpack("<BBHBBH", hdr)
+ #self.log(f"<L:{hlen} T:{mtype:02x} S:{size:04x} D:{devid}")
+ assert hlen == 8
+ #assert mtype == 0x12
+ data = self.dockchannel.read(size)
+ checksum = struct.unpack("<I", self.dockchannel.read(4))[0]
+ expect = self.checksum(hdr + data)
+ if expect != checksum:
+ self.log(f"Checksum error: expected {expect:08x}, got {checksum:08x}")
+ return devid, data
+
+ def send(self, iface, seq, msg):
+ if len(msg) % 4:
+ msg += bytes(4 - len(msg) % 4)
+ hdr = struct.pack("<BBHBBH", 8, 0x11, len(msg), seq, iface, 0)
+ checksum = self.checksum(hdr + msg)
+ pkt = hdr + msg + struct.pack("<I", checksum)
+ self.dockchannel.write(pkt)
+ self.mtp.work_pending()
+
+ def work_pending(self):
+ self.mtp.work_pending()
+ while self.dockchannel.rx_count != 0:
+ self.work()
+ self.mtp.work_pending()
+
+ def work(self):
+ devid, pkt = self.read_pkt()
+ self.iface[devid].packet(pkt)
+
+ def wait_init(self, name):
+ self.log(f"Waiting for {name}...")
+ while not hasattr(self, name) or not getattr(self, name).initialized:
+ self.work()
+
+ def log(self, m):
+ print("[MTP]" + m)
diff --git a/tools/proxyclient/m1n1/fw/pmp.py b/tools/proxyclient/m1n1/fw/pmp.py
new file mode 100644
index 0000000..fbfef45
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/pmp.py
@@ -0,0 +1,168 @@
+# SPDX-License-Identifier: MIT
+import struct
+
+from ..utils import *
+
+from .asc import StandardASC
+from .asc.base import *
+
+class PMPMessage(Register64):
+ TYPE = 56, 44
+
+class PMP_Startup(PMPMessage):
+ TYPE = 56, 44, Constant(0x00)
+
+class PMP_Configure(PMPMessage):
+ TYPE = 56, 44, Constant(0x10)
+ DVA = 47, 0
+
+class PMP_Configure_Ack(PMPMessage):
+ TYPE = 56, 44, Constant(0x20)
+ UNK = 47, 0
+
+class PMP_Init1(PMPMessage):
+ TYPE = 56, 44, Constant(0x200)
+ UNK1 = 43, 16
+ UNK2 = 15, 0
+
+class PMP_Init1_Ack(PMPMessage):
+ TYPE = 56, 44, Constant(0x201)
+ UNK1 = 43, 16
+ UNK2 = 15, 0
+
+class PMP_Init2(PMPMessage):
+ TYPE = 56, 44, Constant(0x202)
+ UNK1 = 43, 16
+ UNK2 = 15, 0
+
+class PMP_Init2_Ack(PMPMessage):
+ TYPE = 56, 44, Constant(0x203)
+ UNK1 = 43, 16
+ UNK2 = 15, 0
+
+class PMP_Unk(PMPMessage):
+ TYPE = 56, 44, Constant(0x100)
+ UNK1 = 43, 16
+ UNK2 = 15, 0
+
+class PMP_Unk_Ack(PMPMessage):
+ TYPE = 56, 44, Constant(0x110)
+ UNK1 = 43, 16
+ UNK2 = 15, 0
+
+class PMP_DevPwr(PMPMessage):
+ TYPE = 56, 44, Constant(0x20e)
+ DEV = 31, 16
+ STATE = 15, 0
+
+class PMP_DevPwr_Sync(PMPMessage):
+ TYPE = 56, 44, Constant(0x208)
+ DEV = 31, 16
+ STATE = 15, 0
+
+class PMP_DevPwr_Ack(PMPMessage):
+ TYPE = 56, 44, Constant(0x209)
+ DEV = 31, 16
+ STATE = 15, 0
+
+class PMPEndpoint(ASCBaseEndpoint):
+ BASE_MESSAGE = PMPMessage
+ SHORT = "pmpep"
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.shmem = self.shmem_dva = None
+ self.init_complete = False
+ self.init1_acked = False
+ self.init2_acked = False
+ self.unk_acked = False
+
+ @msg_handler(0x00, PMP_Startup)
+ def Startup(self, msg):
+ self.log("Starting up")
+
+ self.shmem, self.shmem_dva = self.asc.ioalloc(0x10000)
+
+ self.send_init_config()
+ return True
+
+ def send_init_config(self):
+ self.asc.p.memset32(self.shmem, 0, 0x10000)
+ dram_config = self.asc.u.adt["arm-io/pmp/iop-pmp-nub"].energy_model_dram_configs
+ self.asc.iface.writemem(self.shmem + 0x2000, dram_config)
+
+ node = self.asc.u.adt["arm-io/pmp"]
+
+ maps = []
+ dva = 0xc0000000
+ for i in range(3, len(node.reg)):
+ addr, size = node.get_reg(i)
+ if size == 0:
+ maps.append(struct.pack("<QQ", 0, 0))
+ continue
+
+ self.asc.dart.iomap_at(0, dva, addr, size)
+ self.log(f"map {addr:#x} -> {dva:#x} [{size:#x}]")
+ maps.append(struct.pack("<QQ", dva, size))
+ dva += align(size, 0x4000)
+
+ chexdump(b"".join(maps))
+
+ self.asc.iface.writemem(self.shmem + 0xe000, b"".join(maps))
+ self.send(PMP_Configure(DVA=self.shmem_dva))
+
+ while not self.init_complete:
+ self.asc.work()
+ return True
+
+ @msg_handler(0x20, PMP_Configure_Ack)
+ def Configure_Ack(self, msg):
+ self.init_complete = True
+
+ props = self.asc.iface.readmem(self.shmem, 0x2000)
+ devinfo = self.asc.iface.readmem(self.shmem + 0x4000, 0x1000)
+ status = self.asc.iface.readmem(self.shmem + 0xc000, 0x100)
+
+ print("PMP Props:")
+ chexdump(props)
+ print("PMP Device Info:")
+ chexdump(devinfo)
+ print("PMP Status:")
+ chexdump(status)
+
+ self.send(PMP_Init1(UNK1=1, UNK2=3))
+ while not self.init1_acked:
+ self.asc.work()
+
+ self.send(PMP_Init2(UNK1=1, UNK2=0))
+ while not self.init2_acked:
+ self.asc.work()
+
+ self.send(PMP_Unk(UNK1=0x3bc, UNK2=2))
+ while not self.unk_acked:
+ self.asc.work()
+
+ return True
+
+ @msg_handler(0x201, PMP_Init1_Ack)
+ def Init1_Ack(self, msg):
+ self.init1_acked = True
+ return True
+
+ @msg_handler(0x203, PMP_Init2_Ack)
+ def Init2_Ack(self, msg):
+ self.init2_acked = True
+ return True
+
+ @msg_handler(0x110, PMP_Unk_Ack)
+ def Unk_Ack(self, msg):
+ self.unk_acked = True
+ return True
+
+
+class PMPClient(StandardASC):
+ pass
+
+ ENDPOINTS = {
+ 0x20: PMPEndpoint,
+ }
diff --git a/tools/proxyclient/m1n1/fw/smc.py b/tools/proxyclient/m1n1/fw/smc.py
new file mode 100644
index 0000000..204501c
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/smc.py
@@ -0,0 +1,202 @@
+# SPDX-License-Identifier: MIT
+import struct
+
+from ..utils import *
+
+from .asc import StandardASC
+from .asc.base import *
+
+SMC_READ_KEY = 0x10
+SMC_WRITE_KEY = 0x11
+SMC_GET_KEY_BY_INDEX = 0x12
+SMC_GET_KEY_INFO = 0x13
+SMC_INITIALIZE = 0x17
+SMC_NOTIFICATION = 0x18
+SMC_RW_KEY = 0x20
+
+class SMCMessage(Register64):
+ TYPE = 7, 0
+ UNK = 11, 8, Constant(0)
+ ID = 15, 12
+
+class SMCInitialize(SMCMessage):
+ TYPE = 7, 0, Constant(SMC_INITIALIZE)
+
+class SMCGetKeyInfo(SMCMessage):
+ TYPE = 7, 0, Constant(SMC_GET_KEY_INFO)
+ KEY = 63, 32
+
+class SMCGetKeyByIndex(SMCMessage):
+ TYPE = 7, 0, Constant(SMC_GET_KEY_BY_INDEX)
+ INDEX = 63, 32
+
+class SMCWriteKey(SMCMessage):
+ TYPE = 7, 0, Constant(SMC_WRITE_KEY)
+ SIZE = 23, 16
+ KEY = 63, 32
+
+class SMCReadKey(SMCMessage):
+ TYPE = 7, 0, Constant(SMC_READ_KEY)
+ SIZE = 23, 16
+ KEY = 63, 32
+
+class SMCReadWriteKey(SMCMessage):
+ TYPE = 7, 0, Constant(SMC_RW_KEY)
+ RSIZE = 23, 16
+ WSIZE = 31, 24
+ KEY = 63, 32
+
+class SMCResult(Register64):
+ RESULT = 7, 0
+ ID = 15, 12
+ SIZE = 31, 16
+ VALUE = 63, 32
+
+class SMCError(Exception):
+ pass
+
+class SMCEndpoint(ASCBaseEndpoint):
+ BASE_MESSAGE = SMCMessage
+ SHORT = "smcep"
+ TYPE_MAP = {
+ "ui64": ("<Q", None),
+ "ui32": ("<I", None),
+ "ui16": ("<H", None),
+ "ui8 ": ("<B", None),
+ "si64": ("<q", None),
+ "si32": ("<i", None),
+ "si16": ("<h", None),
+ "si8 ": ("<b", None),
+ "flag": ("<B", None),
+ "flt ": ("<f", None),
+ "hex_": (None, hexdump),
+ "ch8*": (None, lambda c: c.split(b"\x00")[0]),
+ "ioft": ("<Q", None),
+ }
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.shmem = None
+ self.msgid = 0
+ self.outstanding = set()
+ self.ret = {}
+
+ def start(self):
+ self.send(SMCInitialize(ID = 0))
+ self.msgid += 1 # important!
+ while self.shmem is None:
+ self.asc.work()
+
+ def new_msgid(self):
+ mid = (self.msgid & 0xF)
+ self.msgid += 1
+ assert(mid not in self.outstanding)
+ self.outstanding.add(mid)
+ return mid
+
+ def cmd(self, cmd):
+ cmd.ID = self.new_msgid()
+ self.send(cmd)
+ while cmd.ID in self.outstanding:
+ self.asc.work()
+ ret = self.ret[cmd.ID]
+ if ret.RESULT != 0:
+ raise SMCError(f"SMC error {ret}", ret)
+ return ret
+
+ def write(self, key, data):
+ key = int.from_bytes(key.encode("ascii"), byteorder="big")
+ self.asc.iface.writemem(self.shmem, data)
+ self.cmd(SMCWriteKey(KEY = key, SIZE = len(data)))
+
+ def read(self, key, size):
+ key = int.from_bytes(key.encode("ascii"), byteorder="big")
+ ret = self.cmd(SMCReadKey(KEY = key, SIZE = size))
+ if size <= 4:
+ return struct.pack("<I", ret.VALUE)[:size]
+ else:
+ return self.asc.iface.readmem(self.shmem, ret.SIZE)
+
+ def rw(self, key, data, outsize):
+ key = int.from_bytes(key.encode("ascii"), byteorder="big")
+ self.asc.iface.writemem(self.shmem, data)
+ ret = self.cmd(SMCReadWriteKey(KEY=key, RSIZE=outsize, WSIZE=len(data)))
+ if outsize <= 4:
+ return struct.pack("<I", ret.VALUE)[:outsize]
+ else:
+ return self.asc.iface.readmem(self.shmem, ret.SIZE)
+
+ def get_key_by_index(self, index):
+ ret = self.cmd(SMCGetKeyByIndex(INDEX = index))
+ key = ret.VALUE.to_bytes(4, byteorder="little").decode("ascii")
+ return key
+
+ def get_key_info(self, key):
+ key = int.from_bytes(key.encode("ascii"), byteorder="big")
+ ret = self.cmd(SMCGetKeyInfo(KEY = key))
+ info = self.asc.iface.readmem(self.shmem, 6)
+ length, type, flags = struct.unpack("B4sB", info)
+ return length, type.decode("ascii"), flags
+
+ def read64(self, key):
+ return struct.unpack("<Q", self.read(key, 8))[0]
+
+ def read32(self, key):
+ return struct.unpack("<I", self.read(key, 4))[0]
+
+ def read16(self, key):
+ return struct.unpack("<H", self.read(key, 2))[0]
+
+ def read8(self, key):
+ return struct.unpack("<B", self.read(key, 1))[0]
+
+ def read32b(self, key):
+ return struct.unpack(">I", self.read(key, 4))[0]
+
+ def write64(self, key, data):
+ self.write(key, struct.pack("<Q", data))
+
+ def write32(self, key, data):
+ self.write(key, struct.pack("<I", data))
+
+ def write16(self, key, data):
+ self.write(key, struct.pack("<H", data))
+
+ def write8(self, key, data):
+ self.write(key, struct.pack("<B", data))
+
+ def rw32(self, key, data):
+ return struct.unpack("<I", self.rw(key, struct.pack("<I", data), 4))[0]
+
+ def read_type(self, key, size, typecode):
+ fmt, func = self.TYPE_MAP.get(typecode, (None, None))
+
+ val = self.read(key, size)
+
+ if fmt:
+ val = struct.unpack(fmt, val)[0]
+ if func:
+ val = func(val)
+ return val
+
+ def handle_msg(self, msg0, msg1):
+ if self.shmem is None:
+ self.log("Starting up")
+ self.shmem = msg0
+ else:
+ msg = SMCResult(msg0)
+ ret = msg.RESULT
+ mid = msg.ID
+ if ret == SMC_NOTIFICATION:
+ self.log(f"Notification: {msg.VALUE:#x}")
+ return True
+ #print(f"msg {mid} return value {ret}")
+ self.outstanding.discard(mid)
+ self.ret[mid] = msg
+
+ return True
+
+class SMCClient(StandardASC):
+ ENDPOINTS = {
+ 0x20: SMCEndpoint,
+ }