165 lines
4.9 KiB
Python
165 lines
4.9 KiB
Python
#!/usr/bin/env python3
|
|
import os
|
|
from fcntl import ioctl
|
|
from time import sleep
|
|
import struct
|
|
from collections import namedtuple
|
|
import cffi
|
|
import datetime
|
|
import sys
|
|
|
|
ffi = cffi.FFI()
|
|
|
|
# Per cbd, "SS310 modem", "shannon310". This is a Samsung Galaxy S7 SM-G930F.
|
|
# ["/sbin/cbd", "-d", "-tss310", "-bm", "-mm", "-P", "platform/155a0000.ufs/by-name/RADIO"]
|
|
|
|
# Aha! If we stay as root, then the security_request(2, 0, 0) fails
|
|
# with error code 11. cbd does setuid(1001) (while retaining
|
|
# capabilities!) before doing security_request(2, 0, 0), and in that
|
|
# case it succeeds with error code 0. HOWEVER it seems like you can
|
|
# ask cbd to stay as root with `-or`, in which case it gets the error
|
|
# code 11 too - which it ignores! The modem seems (?) well-configured
|
|
# afterward, so perhaps it's not required?
|
|
|
|
def mkstruct(name, fields, spec):
|
|
cls = namedtuple(name, fields)
|
|
def iter_load(bs):
|
|
return map(cls._make, struct.iter_unpack(spec, bs))
|
|
cls.iter_load = iter_load
|
|
def load(bs):
|
|
return cls._make(struct.unpack(spec, bs))
|
|
cls.load = load
|
|
def iter_save(items):
|
|
return b''.join(struct.pack(spec, *item) for item in items)
|
|
cls.iter_save = iter_save
|
|
def save(item):
|
|
return struct.pack(spec, *item)
|
|
cls.save = save
|
|
return cls
|
|
|
|
TOC = mkstruct('TOC', 'name offset loadaddr size crc entryid', '<12sIIIII')
|
|
|
|
# struct modem_firmware
|
|
FirmwareChunk = mkstruct('FirmwareChunk', 'binary size m_offset b_offset mode len', '<QIIIII')
|
|
|
|
# struct modem_sec_req
|
|
SecReq = mkstruct('SecReq', 'mode size_boot size_main', '<III4x')
|
|
|
|
def get_srinfo(fd):
|
|
buf = bytearray(struct.pack('<I1020x', 1020)) # SHMEM_SRINFO_SIZE in link_device_shmem.c
|
|
print('get_srinfo', ioctl(fd, 0x6f45, buf))
|
|
return buf
|
|
|
|
# enum modem_state from modem_prj.h
|
|
enum_modem_state = [
|
|
'STATE_OFFLINE',
|
|
'STATE_CRASH_RESET',
|
|
'STATE_CRASH_EXIT',
|
|
'STATE_BOOTING',
|
|
'STATE_ONLINE',
|
|
'STATE_NV_REBUILDING',
|
|
'STATE_LOADER_DONE',
|
|
'STATE_SIM_ATTACH',
|
|
'STATE_SIM_DETACH',
|
|
'STATE_CRASH_WATCHDOG',
|
|
]
|
|
|
|
def modem_status(fd):
|
|
return enum_modem_state[ioctl(fd, 0x6f27)]
|
|
|
|
def modem_on(fd):
|
|
print('modem_on', ioctl(fd, 0x6f19))
|
|
|
|
def modem_boot_on(fd):
|
|
print('modem_boot_on', ioctl(fd, 0x6f22))
|
|
|
|
def modem_boot_off(fd):
|
|
print('modem_boot_off', ioctl(fd, 0x6f23))
|
|
|
|
def modem_dl_start(fd):
|
|
print('modem_dl_start', ioctl(fd, 0x6f28))
|
|
|
|
def modem_reset(fd):
|
|
print('modem_reset', ioctl(fd, 0x6f21))
|
|
|
|
def security_request(fd, req):
|
|
print(req, ioctl(fd, 0x6f53, bytearray(req.save())))
|
|
|
|
def send_chunk(fd, total_size, load_offset, chunk, file_offset):
|
|
p = ffi.from_buffer(chunk)
|
|
mode = 0 ## cbd uses mode 0 for all three uploads
|
|
mf = FirmwareChunk(int(ffi.cast('long long', p)), total_size, load_offset, file_offset, mode, len(chunk))
|
|
print(mf, ioctl(fd, 0x6f40, bytearray(mf.save())))
|
|
|
|
def send_region(fd, fh, entry, base_loadaddr):
|
|
load_offset = entry.loadaddr - base_loadaddr
|
|
file_offset = entry.offset
|
|
fh.seek(file_offset)
|
|
total_size = entry.size
|
|
remaining = total_size
|
|
while remaining > 0:
|
|
chunksize = min(remaining, 62 * 1024)
|
|
chunk = fh.read(chunksize)
|
|
send_chunk(fd, total_size, load_offset, chunk, file_offset)
|
|
load_offset = load_offset + chunksize
|
|
file_offset = file_offset + chunksize
|
|
remaining = remaining - chunksize
|
|
|
|
print('poke_modem')
|
|
|
|
boot0_path = '/dev/umts_boot0'
|
|
firmware_partition = '/dev/disk/by-partlabel/RADIO'
|
|
nv_data_path = '/efs/nv_data.bin'
|
|
|
|
with open('/sys/power/wake_lock', 'wb') as f:
|
|
f.write(b'ss310')
|
|
|
|
boot0_fd = os.open(boot0_path, os.O_RDWR)
|
|
|
|
with open(firmware_partition, 'rb') as f:
|
|
toc = list(TOC.iter_load(f.read(512)))
|
|
|
|
(boot_toc_entry, main_toc_entry, nv_toc_entry) = toc[1:4]
|
|
print(toc[0])
|
|
print(boot_toc_entry)
|
|
print(main_toc_entry)
|
|
print(nv_toc_entry)
|
|
|
|
# get_srinfo(boot0_fd)
|
|
# modem_status(boot0_fd)
|
|
modem_reset(boot0_fd)
|
|
security_request(boot0_fd, SecReq(2, 0, 0))
|
|
|
|
with open(firmware_partition, 'rb') as f:
|
|
send_region(boot0_fd, f, boot_toc_entry, boot_toc_entry.loadaddr)
|
|
send_region(boot0_fd, f, main_toc_entry, boot_toc_entry.loadaddr)
|
|
with open(nv_data_path, 'rb') as f:
|
|
send_region(boot0_fd, f, nv_toc_entry, boot_toc_entry.loadaddr)
|
|
|
|
security_request(boot0_fd, SecReq(0, boot_toc_entry.size, main_toc_entry.size))
|
|
modem_on(boot0_fd)
|
|
modem_boot_on(boot0_fd)
|
|
modem_dl_start(boot0_fd)
|
|
|
|
os.write(boot0_fd, bytearray(struct.pack('I', 0x0000900d)))
|
|
print(hex(struct.unpack('I', os.read(boot0_fd, 4))[0]))
|
|
os.write(boot0_fd, bytearray(struct.pack('I', 0x00009f00)))
|
|
print(hex(struct.unpack('I', os.read(boot0_fd, 4))[0]))
|
|
|
|
modem_boot_off(boot0_fd)
|
|
|
|
os.close(boot0_fd)
|
|
|
|
with open('/sys/power/wake_unlock', 'wb') as f:
|
|
f.write(b'ss310')
|
|
|
|
sys.stdout.flush()
|
|
with open(boot0_path, 'rb') as f:
|
|
while True:
|
|
i = f.read(512)
|
|
if len(i) > 0:
|
|
print(str(datetime.datetime.now()), '({:03x})'.format(len(i)), modem_status(f.raw.fileno()), i.hex())
|
|
sys.stdout.flush()
|
|
else:
|
|
sleep(1)
|