diff options
| author | magh <magh@maghmogh.com> | 2023-03-06 18:44:55 -0600 |
|---|---|---|
| committer | magh <magh@maghmogh.com> | 2023-03-06 18:44:55 -0600 |
| commit | e80d9d8871b325a04b18f90a9ea4bb7fd148fb25 (patch) | |
| tree | 79dbdb8506b7ff1e92549188d1b94cfc0b3503ae /tools/proxyclient/m1n1/fw | |
Diffstat (limited to 'tools/proxyclient/m1n1/fw')
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, + } |
