mirror of
https://github.com/AsahiLinux/m1n1
synced 2024-11-10 09:44:13 +00:00
m1n1.hw/fw.asc: Add basic ASC driver and RTKit handling
Signed-off-by: Hector Martin <marcan@marcan.st>
This commit is contained in:
parent
7b01b0525b
commit
27a0a15eee
9 changed files with 550 additions and 0 deletions
68
proxyclient/m1n1/fw/asc/__init__.py
Normal file
68
proxyclient/m1n1/fw/asc/__init__.py
Normal file
|
@ -0,0 +1,68 @@
|
|||
# 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
|
||||
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):
|
||||
super().__init__(u, asc_base)
|
||||
self.remote_eps = set()
|
||||
self.add_ep(0, ASCManagementEndpoint(self, 0))
|
||||
self.dart = dart
|
||||
self.eps = []
|
||||
self.epcls = {}
|
||||
|
||||
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 iomap(self, addr, size):
|
||||
return 0xf00000000 | self.dart.iomap(0, addr, size)
|
||||
|
||||
def ioalloc(self, size):
|
||||
paddr = self.u.memalign(0x4000, size)
|
||||
dva = self.iomap(paddr, size)
|
||||
return paddr, dva
|
||||
|
||||
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):
|
||||
self.mgmt.start()
|
||||
|
||||
__all__.extend(k for k, v in globals().items()
|
||||
if (callable(v) or isinstance(v, type)) and v.__module__ == __name__)
|
53
proxyclient/m1n1/fw/asc/base.py
Normal file
53
proxyclient/m1n1/fw/asc/base.py
Normal file
|
@ -0,0 +1,53 @@
|
|||
# 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 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 log(self, msg):
|
||||
print(f"[{self.name}] {msg}")
|
41
proxyclient/m1n1/fw/asc/crash.py
Normal file
41
proxyclient/m1n1/fw/asc/crash.py
Normal file
|
@ -0,0 +1,41 @@
|
|||
# SPDX-License-Identifier: MIT
|
||||
from .base import *
|
||||
from ...utils import *
|
||||
|
||||
class CrashLogMessage(Register64):
|
||||
TYPE = 63, 44
|
||||
|
||||
class CrashLog_TranslateDva(Register64):
|
||||
TYPE = 63, 44, Constant(0x104)
|
||||
ADDR = 43, 0
|
||||
|
||||
class CrashLog_Crashed(Register64):
|
||||
TYPE = 63, 44, Constant(0x103)
|
||||
DVA = 43, 0
|
||||
|
||||
class ASCCrashLogEndpoint(ASCBaseEndpoint):
|
||||
SHORT = "crash"
|
||||
BASE_MESSAGE = CrashLogMessage
|
||||
|
||||
@msg_handler(0x104, CrashLog_TranslateDva)
|
||||
def TranslateDva(self, msg):
|
||||
ranges = self.asc.dart.iotranslate(0, msg.ADDR & 0xffffffff, 4096)
|
||||
assert len(ranges) == 1
|
||||
self.crashbuf = ranges[0][0]
|
||||
self.log(f"Translate {msg.ADDR:#x} -> {self.crashbuf:#x}")
|
||||
self.send(CrashLog_TranslateDva(ADDR=self.crashbuf))
|
||||
return True
|
||||
|
||||
def crash_soft(self):
|
||||
self.send(0x40)
|
||||
|
||||
def crash_hard(self):
|
||||
self.send(0x22)
|
||||
|
||||
@msg_handler(0x103, CrashLog_Crashed)
|
||||
def Crashed(self, msg):
|
||||
self.log(f"Crashed!")
|
||||
crashdata = self.asc.dart.ioread(0, msg.DVA & 0xffffffff, 2048)
|
||||
chexdump(crashdata)
|
||||
|
||||
return True
|
50
proxyclient/m1n1/fw/asc/ioreporting.py
Normal file
50
proxyclient/m1n1/fw/asc/ioreporting.py
Normal file
|
@ -0,0 +1,50 @@
|
|||
# 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)
|
||||
BUFTYPE = 51, 44, Constant(4)
|
||||
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!")
|
||||
|
||||
size = 0x4000
|
||||
self.iobuffer, self.iobuffer_dva = self.asc.ioalloc(size)
|
||||
self.log(f"buf {self.iobuffer:#x} / {self.iobuffer_dva:#x}")
|
||||
self.send(IOReporting_GetBuf(DVA=self.iobuffer_dva))
|
||||
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, 0x4000)
|
||||
#chexdump(buf)
|
||||
self.send(IOReporting_Report())
|
||||
return True
|
38
proxyclient/m1n1/fw/asc/kdebug.py
Normal file
38
proxyclient/m1n1/fw/asc/kdebug.py
Normal file
|
@ -0,0 +1,38 @@
|
|||
# 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 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
|
||||
|
||||
def start(self):
|
||||
self.iobuffer0 = None
|
||||
self.iobuffer1 = None
|
||||
self.iobuffer0_iova = None
|
||||
self.iobuffer1_iova = None
|
||||
self.send(KDebugStart())
|
112
proxyclient/m1n1/fw/asc/mgmt.py
Normal file
112
proxyclient/m1n1/fw/asc/mgmt.py
Normal file
|
@ -0,0 +1,112 @@
|
|||
# SPDX-License-Identifier: MIT
|
||||
from .base import *
|
||||
from ...utils import *
|
||||
|
||||
## Management endpoint
|
||||
class ManagementMessage(Register64):
|
||||
TYPE = 59, 52
|
||||
|
||||
class Mgmt_Hello(ManagementMessage):
|
||||
TYPE = 59, 52, Constant(1)
|
||||
UNK1 = 31, 16
|
||||
UNK2 = 15, 0
|
||||
|
||||
class Mgmt_HelloAck(ManagementMessage):
|
||||
TYPE = 59, 52, Constant(2)
|
||||
UNK1 = 31, 16
|
||||
UNK2 = 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
|
||||
|
||||
class Mgmt_Init(ManagementMessage):
|
||||
TYPE = 59, 52, Constant(6)
|
||||
UNK = 15, 0
|
||||
|
||||
class Mgmt_BootDone(ManagementMessage):
|
||||
TYPE = 59, 52, Constant(7)
|
||||
|
||||
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_StartSyslog(ManagementMessage):
|
||||
TYPE = 59, 52, Constant(0xb)
|
||||
UNK1 = 15, 0
|
||||
|
||||
class ASCManagementEndpoint(ASCBaseEndpoint):
|
||||
BASE_MESSAGE = ManagementMessage
|
||||
SHORT = "mgmt"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.syslog_started = False
|
||||
self.boot_done = False
|
||||
|
||||
@msg_handler(1, Mgmt_Hello)
|
||||
def Hello(self, msg):
|
||||
self.send(Mgmt_HelloAck(UNK1=msg.UNK1, UNK2=msg.UNK2))
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
return True
|
||||
|
||||
@msg_handler(0xb, Mgmt_StartSyslog)
|
||||
def StartSyslogAck(self, msg):
|
||||
self.syslog_started = True
|
||||
return True
|
||||
|
||||
@msg_handler(7, Mgmt_BootDone)
|
||||
def BootDone(self, msg):
|
||||
#self.start_syslog()
|
||||
self.boot_done = True
|
||||
return True
|
||||
|
||||
@msg_handler(4, Mgmt_Pong)
|
||||
def Pong(self, msg):
|
||||
return True
|
||||
|
||||
def start(self):
|
||||
self.send(Mgmt_Init(UNK=0x220))
|
||||
while not self.boot_done or not self.syslog_started:
|
||||
self.asc.work()
|
||||
self.log("startup complete")
|
||||
|
||||
def start_ep(self, epno):
|
||||
self.send(Mgmt_StartEP(EP=epno, FLAG=1))
|
||||
|
||||
def start_syslog(self):
|
||||
self.send(Mgmt_StartSyslog(UNK1=0x20))
|
||||
|
||||
def ping(self):
|
||||
self.send(Mgmt_Ping())
|
30
proxyclient/m1n1/fw/asc/oslog.py
Normal file
30
proxyclient/m1n1/fw/asc/oslog.py
Normal file
|
@ -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
|
70
proxyclient/m1n1/fw/asc/syslog.py
Normal file
70
proxyclient/m1n1/fw/asc/syslog.py
Normal file
|
@ -0,0 +1,70 @@
|
|||
# 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)
|
||||
UNK1 = 51, 44, Constant(3)
|
||||
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 = 0x4000
|
||||
|
||||
if self.iobuffer:
|
||||
print("WARNING: trying to reset iobuffer!")
|
||||
|
||||
self.iobuffer, self.iobuffer_dva = self.asc.ioalloc(size)
|
||||
self.log(f"buf {self.iobuffer:#x} / {self.iobuffer_dva:#x}")
|
||||
self.send(Syslog_GetBuf(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.iface.readmem(self.iobuffer + 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
|
||||
|
||||
def start(self):
|
||||
self.asc.mgmt.start_syslog()
|
88
proxyclient/m1n1/hw/asc.py
Normal file
88
proxyclient/m1n1/hw/asc.py
Normal file
|
@ -0,0 +1,88 @@
|
|||
# SPDX-License-Identifier: MIT
|
||||
from ..utils import *
|
||||
|
||||
class R_OUTBOX_CTRL(Register32):
|
||||
EMPTY = 17
|
||||
FULL = 16
|
||||
|
||||
class R_INBOX_CTRL(Register32):
|
||||
EMPTY = 17
|
||||
FULL = 16
|
||||
ENABLE = 1
|
||||
|
||||
class R_CPU_CONTROL(Register32):
|
||||
RUN = 4
|
||||
|
||||
class R_INBOX1(Register64):
|
||||
EP = 7, 0
|
||||
|
||||
class R_OUTBOX1(Register64):
|
||||
OUTCNT = 56, 52
|
||||
INCNT = 51, 48
|
||||
OUTPTR = 47, 44
|
||||
INPTR = 43, 40
|
||||
EP = 7, 0
|
||||
|
||||
class ASCRegs(RegMap):
|
||||
CPU_CONTROL = 0x0044, R_CPU_CONTROL
|
||||
INBOX_CTRL = 0x8110, R_INBOX_CTRL
|
||||
OUTBOX_CTRL = 0x8114, R_OUTBOX_CTRL
|
||||
INBOX0 = 0x8800, Register64
|
||||
INBOX1 = 0x8808, R_INBOX1
|
||||
OUTBOX0 = 0x8830, Register64
|
||||
OUTBOX1 = 0x8838, R_OUTBOX1
|
||||
|
||||
class ASC:
|
||||
def __init__(self, u, asc_base):
|
||||
self.u = u
|
||||
self.p = u.proxy
|
||||
self.iface = u.iface
|
||||
self.asc = ASCRegs(u, asc_base)
|
||||
self.epmap = {}
|
||||
|
||||
def recv(self):
|
||||
if self.asc.OUTBOX_CTRL.reg.EMPTY:
|
||||
return None, None
|
||||
|
||||
msg0 = self.asc.OUTBOX0.val
|
||||
msg1 = R_INBOX1(self.asc.OUTBOX1.val)
|
||||
print(f"< {msg1.EP:02x}:{msg0:#x}")
|
||||
return msg0, msg1
|
||||
|
||||
def send(self, msg0, msg1):
|
||||
self.asc.INBOX0.val = msg0
|
||||
self.asc.INBOX1.val = msg1
|
||||
|
||||
print(f"> {msg1.EP:02x}:{msg0}")
|
||||
|
||||
while self.asc.INBOX_CTRL.reg.FULL:
|
||||
pass
|
||||
|
||||
def boot(self):
|
||||
self.asc.CPU_CONTROL.set(RUN=1)
|
||||
self.asc.CPU_CONTROL.set(RUN=0)
|
||||
|
||||
def add_ep(self, idx, ep):
|
||||
self.epmap[idx] = ep
|
||||
setattr(self, ep.SHORT, ep)
|
||||
|
||||
def work(self):
|
||||
if self.asc.OUTBOX_CTRL.reg.EMPTY:
|
||||
return True
|
||||
|
||||
msg0, msg1 = self.recv()
|
||||
|
||||
handled = False
|
||||
|
||||
ep = self.epmap.get(msg1.EP, None)
|
||||
if ep:
|
||||
handled = ep.handle_msg(msg0, msg1)
|
||||
|
||||
if not handled:
|
||||
print(f"unknown message: {msg0:#16x} / {msg1}")
|
||||
|
||||
return handled
|
||||
|
||||
def work_forever(self):
|
||||
while self.work():
|
||||
pass
|
Loading…
Reference in a new issue