Skip to content

Instantly share code, notes, and snippets.

@tonyg
Created September 9, 2020 21:06
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save tonyg/4ea14f4dfe414422c0648c6e0a8bcb5d to your computer and use it in GitHub Desktop.
Quick-and-dirty plain-kernel-interface python modem boot and operation scripts for Samsung Galaxy S7
#!/usr/bin/env python3
# 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"]
boot0_path = '/dev/umts_boot0'
firmware_partition = '/dev/disk/by-partlabel/RADIO' ## maps to /dev/sda8 on my phone
nv_data_path = '/efs/nv_data.bin' ## mount /dev/disk/by-partlabel/EFS (/dev/sda3 for me) on /efs
import cffi
import datetime
import os
import struct
import sys
from collections import namedtuple
from fcntl import ioctl
from time import sleep
ffi = cffi.FFI()
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')
# 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 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('qnd_cbd')
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(boot_toc_entry)
print(main_toc_entry)
print(nv_toc_entry)
with open('/sys/power/wake_lock', 'wb') as f:
f.write(b'ss310')
boot0_fd = os.open(boot0_path, os.O_RDWR)
print('modem_reset', ioctl(boot0_fd, 0x6f21))
# 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?
#
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))
print('modem_on', ioctl(boot0_fd, 0x6f19))
print('modem_boot_on', ioctl(boot0_fd, 0x6f22))
print('modem_dl_start', ioctl(boot0_fd, 0x6f28))
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]))
print('modem_boot_off', ioctl(boot0_fd, 0x6f23))
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:
status = enum_modem_state[ioctl(f.raw.fileno(), 0x6f27)]
print(str(datetime.datetime.now()), '({:03x})'.format(len(i)), status, i.hex())
sys.stdout.flush()
else:
sleep(1)
#!/usr/bin/env python3
ipc0_path = '/dev/umts_ipc0'
rfs0_path = '/dev/umts_rfs0'
from collections import namedtuple
from fcntl import ioctl
import cffi
import datetime
import os
import select
import struct
import sys
ffi = cffi.FFI()
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
# struct sipc_fmt_hdr, modem_prj.h
Packet = mkstruct('Packet', 'len msg_seq ack_seq main_cmd sub_cmd cmd_type', '<HBBBBB')
# 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',
]
class Driver(object):
def __init__(self, name, fd):
self.name = name
self.fd = fd
def read_ready(self):
i = os.read(self.fd, 65536)
self.decode(i)
print(str(datetime.datetime.now()), self.name, '({:04x})'.format(len(i)), self.statusreport())
def decode(self, bs):
raise Exception('subclassResponsibility')
def statusreport(self):
raise Exception('subclassResponsibility')
class IpcDriver(Driver):
def __init__(self, *args):
super().__init__(*args)
self.head = None
self.body = b''
self.leftover = b''
def decode(self, bs):
self.head = Packet.load(bs[:7])
bodysize = self.head.len - 7
self.body = bs[7:self.head.len]
self.leftover = bs[self.head.len:]
def statusreport(self):
status = enum_modem_state[ioctl(self.fd, 0x6f27)]
return ' '.join([status, repr(self.head), self.body.hex(), repr(self.body), self.leftover.hex()])
class RfsDriver(Driver):
def __init__(self, *args):
super().__init__(*args)
self.packet = b''
def decode(self, bs):
self.packet = bs
def statusreport(self):
return self.packet.hex()
ipc0_fd = os.open(ipc0_path, os.O_RDWR)
rfs0_fd = os.open(rfs0_path, os.O_RDWR)
drivers = {}
drivers[ipc0_fd] = IpcDriver('ipc0', ipc0_fd)
drivers[rfs0_fd] = RfsDriver('rfs0', rfs0_fd)
while True:
(readfds, _writefds, errorfds) = select.select([ipc0_fd, rfs0_fd], [], [ipc0_fd, rfs0_fd])
if len(errorfds) != 0:
print('AIEEE', errorfds)
break
for fd in readfds:
drivers[fd].read_ready()
sys.stdout.flush()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment