ams.tma: restore tma code to debugger_dev branch

This commit is contained in:
Michael Scire
2019-03-05 08:48:21 -08:00
parent a38927ec04
commit 37025258c6
47 changed files with 4511 additions and 3 deletions

View File

@@ -0,0 +1,35 @@
# Copyright (c) 2018 Atmosphere-NX
#
# This program is free software; you can redistribute it and/or modify it
# under the terms and conditions of the GNU General Public License,
# version 2, as published by the Free Software Foundation.
#
# This program is distributed in the hope it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License
from UsbConnection import UsbConnection
import sys, time
from Packet import Packet
import ServiceId
def main(argc, argv):
with UsbConnection(None) as c:
print 'Waiting for connection...'
c.wait_connected()
print 'Connected!'
print 'Reading atmosphere/BCT.ini...'
c.intf.send_packet(Packet().set_service(ServiceId.TARGETIO_SERVICE).set_task(0x01000000).set_cmd(2).write_str('atmosphere/BCT.ini').write_u64(0x109).write_u64(0))
resp = c.intf.read_packet()
res_packet = c.intf.read_packet()
read_res, size_read = resp.read_u32(), resp.read_u32()
print 'Final Result: 0x%x' % res_packet.read_u32()
print 'Size Read: 0x%x' % size_read
print 'Data:\n%s' % resp.body[resp.offset:]
return 0
if __name__ == '__main__':
sys.exit(main(len(sys.argv), sys.argv))

View File

@@ -0,0 +1,120 @@
# Copyright (c) 2018 Atmosphere-NX
#
# This program is free software; you can redistribute it and/or modify it
# under the terms and conditions of the GNU General Public License,
# version 2, as published by the Free Software Foundation.
#
# This program is distributed in the hope it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import zlib
import ServiceId
from struct import unpack as up, pack as pk
HEADER_SIZE = 0x28
def crc32(s):
return zlib.crc32(s) & 0xFFFFFFFF
class Packet():
def __init__(self):
self.service = 0
self.task = 0
self.cmd = 0
self.continuation = 0
self.version = 0
self.body_len = 0
self.body = ''
self.offset = 0
def load_header(self, header):
assert len(header) == HEADER_SIZE
self.service, self.task, self.cmd, self.continuation, self.version, self.body_len, \
_, self.body_chk, self.hdr_chk = up('<IIHBBI16sII', header)
if crc32(header[:-4]) != self.hdr_chk:
raise ValueError('Invalid header checksum in received packet!')
def load_body(self, body):
assert len(body) == self.body_len
if crc32(body) != self.body_chk:
raise ValueError('Invalid body checksum in received packet!')
self.body = body
def get_data(self):
assert len(self.body) == self.body_len and self.body_len <= 0xE000
self.body_chk = crc32(self.body)
hdr = pk('<IIHBBIIIIII', self.service, self.task, self.cmd, self.continuation, self.version, self.body_len, 0, 0, 0, 0, self.body_chk)
self.hdr_chk = crc32(hdr)
hdr += pk('<I', self.hdr_chk)
return hdr + self.body
def set_service(self, srv):
if type(srv) is str:
self.service = ServiceId.hash(srv)
else:
self.service = srv
return self
def set_task(self, t):
self.task = t
return self
def set_cmd(self, x):
self.cmd = x
return self
def set_continuation(self, c):
self.continuation = c
return self
def set_version(self, v):
self.version = v
return self
def reset_offset(self):
self.offset = 0
return self
def write_str(self, s):
if s[-1] != '\x00':
s += '\x00'
self.body += s
self.body_len += len(s)
return self
def write_u8(self, x):
self.body += pk('<B', x & 0xFF)
self.body_len += 1
return self
def write_u16(self, x):
self.body += pk('<H', x & 0xFFFF)
self.body_len += 2
return self
def write_u32(self, x):
self.body += pk('<I', x & 0xFFFFFFFF)
self.body_len += 4
return self
def write_u64(self, x):
self.body += pk('<Q', x & 0xFFFFFFFFFFFFFFFF)
self.body_len += 8
return self
def read_str(self):
s = ''
while self.body[self.offset] != '\x00' and self.offset < self.body_len:
s += self.body[self.offset]
self.offset += 1
if self.offset < self.body_len and self.body[self.offset] == '\x00':
self.offset += 1
def read_u8(self):
x, = up('<B', self.body[self.offset:self.offset+1])
self.offset += 1
return x
def read_u16(self):
x, = up('<H', self.body[self.offset:self.offset+2])
self.offset += 2
return x
def read_u32(self):
x, = up('<I', self.body[self.offset:self.offset+4])
self.offset += 4
return x
def read_u64(self):
x, = up('<Q', self.body[self.offset:self.offset+8])
self.offset += 8
return x
def read_struct(self, format, sz):
x = up(format, self.body[self.offset:self.offset+sz])
self.offset += sz
return x

View File

@@ -0,0 +1,30 @@
# Copyright (c) 2018 Atmosphere-NX
#
# This program is free software; you can redistribute it and/or modify it
# under the terms and conditions of the GNU General Public License,
# version 2, as published by the Free Software Foundation.
#
# This program is distributed in the hope it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
def hash(s):
h = ord(s[0]) & 0xFFFFFFFF
for c in s:
h = ((1000003 * h) ^ ord(c)) & 0xFFFFFFFF
h ^= len(s)
return h
USB_QUERY_TARGET = hash("USBQueryTarget")
USB_SEND_HOST_INFO = hash("USBSendHostInfo")
USB_CONNECT = hash("USBConnect")
USB_DISCONNECT = hash("USBDisconnect")
ATMOSPHERE_TEST_SERVICE = hash("AtmosphereTestService")
SETTINGS_SERVICE = hash("SettingsService")
TARGETIO_SERVICE = hash("TIOService")

View File

@@ -0,0 +1,134 @@
# Copyright (c) 2018 Atmosphere-NX
#
# This program is free software; you can redistribute it and/or modify it
# under the terms and conditions of the GNU General Public License,
# version 2, as published by the Free Software Foundation.
#
# This program is distributed in the hope it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from UsbInterface import UsbInterface
from threading import Thread, Condition
from collections import deque
import time
import ServiceId
from Packet import Packet
class UsbConnection(UsbInterface):
# Auto connect thread func.
def auto_connect(connection):
while not connection.is_connected():
try:
connection.connect(UsbInterface())
except ValueError as e:
continue
def recv_thread(connection):
while connection.is_connected():
try:
connection.recv_packet()
except Exception as e:
print 'An exception occurred:'
print 'Type: '+e.__class__.__name__
print 'Msg: '+str(e)
connection.disconnect()
connection.send_packet(None)
def send_thread(connection):
while connection.is_connected():
try:
next_packet = connection.get_next_send_packet()
if next_packet is not None:
connection.intf.send_packet(next_packet)
else:
connection.disconnect()
except Exception as e:
print 'An exception occurred:'
print 'Type: '+e.__class__.__name__
print 'Msg: '+str(e)
connection.disconnect()
def __init__(self, manager):
self.manager = manager
self.connected = False
self.intf = None
self.conn_lock, self.send_lock = Condition(), Condition()
self.send_queue = deque()
def __enter__(self):
self.conn_thrd = Thread(target=UsbConnection.auto_connect, args=(self,))
self.conn_thrd.daemon = True
self.conn_thrd.start()
return self
def __exit__(self, type, value, traceback):
self.disconnect()
time.sleep(1)
print 'Closing!'
time.sleep(1)
def wait_connected(self):
self.conn_lock.acquire()
if not self.is_connected():
self.conn_lock.wait()
self.conn_lock.release()
def is_connected(self):
return self.connected
def connect(self, intf):
# This indicates we have a connection.
self.conn_lock.acquire()
assert not self.connected
self.intf = intf
try:
# Perform Query + Connection handshake
print 'Performing handshake...'
self.intf.send_packet(Packet().set_service(ServiceId.USB_QUERY_TARGET))
query_resp = self.intf.read_packet()
print 'Found Switch, Protocol version 0x%x' % query_resp.read_u32()
self.intf.send_packet(Packet().set_service(ServiceId.USB_SEND_HOST_INFO).write_u32(0).write_u32(0))
self.intf.send_packet(Packet().set_service(ServiceId.USB_CONNECT))
resp = self.intf.read_packet()
# Spawn threads
self.recv_thrd = Thread(target=UsbConnection.recv_thread, args=(self,))
self.send_thrd = Thread(target=UsbConnection.send_thread, args=(self,))
self.recv_thrd.daemon = True
self.send_thrd.daemon = True
self.recv_thrd.start()
self.send_thrd.start()
self.connected = True
finally:
# Finish connection.
self.conn_lock.notify()
self.conn_lock.release()
def disconnect(self):
self.conn_lock.acquire()
if self.connected:
self.connected = False
self.intf.send_packet(Packet().set_service(ServiceId.USB_DISCONNECT))
self.conn_lock.release()
def recv_packet(self):
packet = self.intf.read_packet()
assert type(packet) is Packet
dat = packet.read_u64()
print('Got Packet: %08x' % dat)
def send_packet(self, packet):
assert type(packet) is Packet
self.send_lock.acquire()
if len(self.send_queue) == 0x40:
self.send_lock.wait()
self.send_queue.append(packet)
if len(self.send_queue) == 1:
self.send_lock.notify()
self.send_lock.release()
def get_next_send_packet(self):
self.send_lock.acquire()
if len(self.send_queue) == 0:
self.send_lock.wait()
packet = self.send_queue.popleft()
if len(self.send_queue) == 0x3F:
self.send_lock.notify()
self.send_lock.release()
return packet

View File

@@ -0,0 +1,62 @@
# Copyright (c) 2018 Atmosphere-NX
#
# This program is free software; you can redistribute it and/or modify it
# under the terms and conditions of the GNU General Public License,
# version 2, as published by the Free Software Foundation.
#
# This program is distributed in the hope it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import usb
import Packet
class UsbInterface():
def __init__(self):
self.dev = usb.core.find(idVendor=0x057e, idProduct=0x3000)
if self.dev is None:
raise ValueError('Device not found')
self.dev.set_configuration()
self.cfg = self.dev.get_active_configuration()
self.intf = usb.util.find_descriptor(self.cfg, bInterfaceClass=0xff, bInterfaceSubClass=0xff, bInterfaceProtocol=0xfc)
assert self.intf is not None
self.ep_in = usb.util.find_descriptor(
self.intf,
custom_match = \
lambda e: \
usb.util.endpoint_direction(e.bEndpointAddress) == \
usb.util.ENDPOINT_IN)
assert self.ep_in is not None
self.ep_out = usb.util.find_descriptor(
self.intf,
custom_match = \
lambda e: \
usb.util.endpoint_direction(e.bEndpointAddress) == \
usb.util.ENDPOINT_OUT)
assert self.ep_out is not None
def close(self):
usb.util.dispose_resources(self.dev)
def blocking_read(self, size):
return ''.join(chr(x) for x in self.ep_in.read(size, 0xFFFFFFFFFFFFFFFF))
def blocking_write(self, data):
self.ep_out.write(data, 0xFFFFFFFFFFFFFFFF)
def read_packet(self):
packet = Packet.Packet()
hdr = self.blocking_read(Packet.HEADER_SIZE)
packet.load_header(hdr)
if packet.body_len:
packet.load_body(self.blocking_read(packet.body_len))
return packet
def send_packet(self, packet):
data = packet.get_data()
self.blocking_write(data[:Packet.HEADER_SIZE])
if (len(data) > Packet.HEADER_SIZE):
self.blocking_write(data[Packet.HEADER_SIZE:])