squeak-phone/devices/samsung-herolte/qnd_cbd.py

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)