2021-12-18 14:20:30 +02:00
#!/usr/bin/env python
import usb
import sys , time
import os
import unicmd
# global params
UNISOC_VID = 0x1782
UNISOC_PID = 0x4d00
2021-12-20 01:16:41 +02:00
UNISOC_FLASH_BASE_ADDR = 0x10000000
2021-12-18 14:20:30 +02:00
MAX_PKT_SIZE = 1024
bSize = 512 # read block size
2021-12-20 16:35:51 +02:00
genTimeout = 120000
2021-12-18 14:20:30 +02:00
# all main procedures
def connect ( vid , pid ):
while True :
dev = usb . core . find ( idVendor = vid , idProduct = pid )
sys . stdout . write ( '.' )
sys . stdout . flush ()
if dev is not None :
print ( ' \n Device connected' )
break
time . sleep ( 0.1 )
dev . set_configuration ()
cfg = dev . get_active_configuration ()
intf = cfg [( 0 , 0 )]
epIn = usb . util . find_descriptor (
intf ,
custom_match = \
lambda e : \
usb . util . endpoint_direction ( e . bEndpointAddress ) == \
usb . util . ENDPOINT_IN )
epOut = usb . util . find_descriptor (
intf ,
custom_match = \
lambda e : \
usb . util . endpoint_direction ( e . bEndpointAddress ) == \
usb . util . ENDPOINT_OUT )
assert epIn is not None
assert epOut is not None
return dev , epIn , epOut
def reqonly ( packet , fdlBooted = False , noCrc = False ):
packet = unicmd . hdlc_encode ( packet , fdlBooted , noCrc )
dev . write ( epOut , packet , genTimeout )
def reqresp ( packet , fdlBooted = False , noCrc = False ):
reqonly ( packet , fdlBooted , noCrc )
resp = bytes ( dev . read ( epIn , bSize , genTimeout ))
return resp
def handshake ( fdlBooted = False ):
resp = reqresp ( unicmd . cmd_sync (), fdlBooted )
rcode , rlen , r = unicmd . resp_decode ( resp , fdlBooted )
if len ( r ):
print ( '>' , r . decode ())
resp = reqresp ( unicmd . cmd_connect (), fdlBooted )
rcode , rlen , r = unicmd . resp_decode ( resp , fdlBooted )
if len ( r ):
print ( '>' , r . decode ())
2021-12-20 16:35:51 +02:00
def send_file_to_addr ( fname , faddr , fdlBooted = False , flashMode = False , fbs = 1024 ):
2021-12-18 14:20:30 +02:00
pSize = MAX_PKT_SIZE
print ( 'Initializing data transfer...' )
2021-12-20 16:35:51 +02:00
dataCrc = 0
2021-12-20 01:16:41 +02:00
if flashMode :
2021-12-20 16:35:51 +02:00
# faddr is our flash offset in this case, and we directly pass the data buffer to the function
2021-12-20 01:16:41 +02:00
pSize = fbs
2021-12-20 16:35:51 +02:00
fdata = fname
flen = len ( fdata )
2021-12-20 01:16:41 +02:00
else :
2021-12-20 16:35:51 +02:00
f = open ( fname , 'rb' )
fdata = f . read ()
f . close ()
flen = len ( fdata )
dataCrc = unicmd . chksum32 ( fdata )
resp = reqresp ( unicmd . cmd_data_start ( faddr , flen , dataCrc ), fdlBooted )
2021-12-18 14:20:30 +02:00
rcode , rlen , r = unicmd . resp_decode ( resp , fdlBooted )
assert rcode == unicmd . BSL_REP_ACK , 'Could not start data transfer, response code is %X ' % rcode
print ( 'Starting data transfer...' )
while fdata :
buf = fdata [: pSize ]
resp = reqresp ( unicmd . cmd_data_send ( buf ), fdlBooted )
rcode , rlen , r = unicmd . resp_decode ( resp , fdlBooted )
assert rcode == unicmd . BSL_REP_ACK , 'Something is wrong and response code is %X , block is %s ' % ( rcode , buf . hex ())
fdata = fdata [ pSize :]
sys . stdout . write ( '.' )
sys . stdout . flush ()
print ( ' \n Ending data transfer...' )
resp = reqresp ( unicmd . cmd_data_end (), fdlBooted )
rcode , rlen , r = unicmd . resp_decode ( resp , fdlBooted )
2021-12-20 16:35:51 +02:00
if not flashMode or ( rcode != unicmd . BSL_FLASH_CFG_ERROR and rcode != unicmd . BSL_WRITE_ERROR ): # on flashing, ignore 0xA2 and 0xA4 errors
2021-12-20 01:16:41 +02:00
assert rcode == unicmd . BSL_REP_ACK , 'Could not finalize data transfer, response code is %X ' % rcode
2021-12-18 14:20:30 +02:00
print ( 'Data transfer successful' )
2021-12-20 01:16:41 +02:00
# readback code implementation
def read_partdata ( partid , size , offset ):
2021-12-18 14:20:30 +02:00
t = b ''
2021-12-20 01:16:41 +02:00
reqonly ( unicmd . cmd_read_flash ( partid , size , offset ), True )
2021-12-18 14:20:30 +02:00
while True :
xr = bytes ( dev . read ( epIn , bSize , genTimeout ))
t += xr
if len ( xr ) < bSize :
break
return t
2021-12-20 01:16:41 +02:00
def read_partition ( partid , partsize , partoffset , outfile , rbblocksize ):
2021-12-18 14:20:30 +02:00
outf = open ( outfile , 'wb' )
psize = partsize
offset = partoffset
2021-12-20 01:16:41 +02:00
print ( 'Dumping %d bytes from partition 0x %X at offset 0x %X to %s ...' % ( partsize , partid , partoffset , outfile ))
2021-12-18 14:20:30 +02:00
bufsize = rbblocksize
while psize > 0 :
if psize < bufsize :
bufsize = psize
2021-12-20 01:16:41 +02:00
resp = read_partdata ( partid , bufsize , offset )
2021-12-18 14:20:30 +02:00
rcode , rlen , r = unicmd . resp_decode ( resp , True )
outf . write ( r )
sys . stdout . write ( '.' )
sys . stdout . flush ()
psize -= rlen
offset += rlen
outf . flush ()
print ( ' \n Partition dumped!' )
2021-12-20 16:35:51 +02:00
# memory eraser and writer
2021-12-20 12:53:02 +02:00
def erase_flash_mem ( size , faddr ):
print ( 'Erasing %d bytes in the flash memory at offset 0x %X ...' % ( size , faddr - UNISOC_FLASH_BASE_ADDR ))
resp = reqresp ( unicmd . cmd_erase_flash ( faddr , size ), True )
rcode , rlen , r = unicmd . resp_decode ( resp , True )
assert rcode == unicmd . BSL_REP_ACK , 'Could not erase flash memory, response code is %X ' % rcode
print ( 'Flash range erased!' )
2021-12-20 16:35:51 +02:00
def write_flash_mem ( infile , offset , blocksize , forceErase ):
startAddr = UNISOC_FLASH_BASE_ADDR + offset
f = open ( infile , 'rb' )
fdata = f . read ()
f . close ()
flen = len ( fdata )
if forceErase :
erase_flash_mem ( flen , startAddr )
send_file_to_addr ( fdata , startAddr , True , True , blocksize )
2021-12-18 14:20:30 +02:00
def auto_int ( x ):
return int ( x , 0 )
# main code start
if __name__ == '__main__' : # main app start
from argparse import ArgumentParser
rootdir = os . path . dirname ( os . path . realpath ( __file__ ))
2021-12-20 01:16:41 +02:00
parser = ArgumentParser ( description = 'UniFlash: an opensource Unisoc SC6531E/F/M phone flash reader/writer' , epilog = '(c) Luxferre 2021 --- No rights reserved <https://unlicense.org>' )
parser . add_argument ( 'mode' , help = 'Operation mode (flash/dump)' )
parser . add_argument ( 'file' , help = 'File to read the flash data from or write the dump into' )
parser . add_argument ( '-p' , '--partid' , type = auto_int , default = 0x80000003 , help = 'partition ID for readback (defaults to 0x80000003 that can address full flash space on SC6531E/F/M)' )
2021-12-18 14:20:30 +02:00
parser . add_argument ( '-s' , '--start' , type = auto_int , default = 0 , help = 'start position (in the partition, defaults to 0)' )
2021-12-20 01:16:41 +02:00
parser . add_argument ( '-l' , '--length' , type = auto_int , default = 0x400000 , help = 'data length in bytes to read/write, defaults to 0x400000' )
2021-12-20 12:53:02 +02:00
parser . add_argument ( '-e' , '--force-erase' , type = bool , default = False , help = 'Erase target flash memory area before writing' )
2021-12-20 01:16:41 +02:00
parser . add_argument ( '-bs' , '--block-size' , type = auto_int , default = 4096 , help = 'Readback/write block size (in bytes), defaults to 4096' )
2021-12-18 14:20:30 +02:00
parser . add_argument ( '-dv' , '--device-vid' , type = auto_int , default = UNISOC_VID , help = 'Override device vendor ID' )
parser . add_argument ( '-dp' , '--device-pid' , type = auto_int , default = UNISOC_PID , help = 'Override device product ID' )
2021-12-21 12:31:37 +02:00
parser . add_argument ( '-fdl1' , '--fdl1-file' , default = rootdir + '/fdls/sc6531efm_generic_0x40004000_fdl1.bin' , help = 'Path to FDL1, defaults to the generic SC6531E/F/M FDL1 shipped with UniFlash' )
2021-12-18 14:20:30 +02:00
parser . add_argument ( '-addr1' , '--fdl1-addr' , type = auto_int , default = 0x40004000 , help = 'Address to load FDL1 into, defaults to 0x40004000' )
2021-12-21 12:31:37 +02:00
parser . add_argument ( '-fdl2' , '--fdl2-file' , default = rootdir + '/fdls/sc6531efm_generic_0x14000000_fdl2.bin' , help = 'Path to FDL2, defaults to the generic SC6531E/F/M FDL2 shipped with UniFlash' )
2021-12-18 14:20:30 +02:00
parser . add_argument ( '-addr2' , '--fdl2-addr' , type = auto_int , default = 0x14000000 , help = 'Address to load FDL2 into, defaults to 0x14000000' )
2021-12-21 12:31:37 +02:00
parser . add_argument ( '-sfdl' , '--single-fdl-file' , default = None , help = 'Path to a single FDL (for old Spreadtrum chipsets)' )
parser . add_argument ( '-saddr' , '--single-fdl-addr' , type = auto_int , default = 0x34000000 , help = 'Address to load the single FDL into, defaults to 0x34000000' )
2021-12-18 14:20:30 +02:00
args = parser . parse_args ()
2021-12-20 01:16:41 +02:00
is_flash = False
if args . mode == 'flash' :
is_flash = True
2021-12-18 14:20:30 +02:00
UNISOC_VID = args . device_vid
UNISOC_PID = args . device_pid
fdl1Addr = args . fdl1_addr
fdl2Addr = args . fdl2_addr
fdl1Name = args . fdl1_file
fdl2Name = args . fdl2_file
2021-12-21 12:31:37 +02:00
fdlSingleName = args . single_fdl_file
fdlSingleAddr = args . single_fdl_addr
2021-12-18 14:20:30 +02:00
outfile = args . file
partitionId = args . partid
readbs = args . block_size
readoffset = args . start
readlen = args . length
2021-12-20 12:53:02 +02:00
forceErase = args . force_erase
2021-12-21 12:31:37 +02:00
singleFdlMode = False
fdl1Label = 'FDL1'
fdl2Label = 'FDL2'
2021-12-18 14:20:30 +02:00
2021-12-21 12:31:37 +02:00
if fdlSingleName is not None :
singleFdlMode = True
fdl1Addr = fdlSingleAddr
fdl1Name = fdlSingleName
fdl1Label = 'FDL'
fdl2Label = 'FDL'
print ( 'Using a single FDL %s , loading to 0x %X ' % ( fdlSingleName , fdlSingleAddr ))
else :
print ( 'Using FDL1 %s , loading to 0x %X ' % ( fdl1Name , fdl1Addr ))
print ( 'Using FDL2 %s , loading to 0x %X ' % ( fdl2Name , fdl2Addr ))
2021-12-18 14:20:30 +02:00
# initial connection
print ( 'Connect the device %X : %X while holding the bootkey...' % ( UNISOC_VID , UNISOC_PID ) )
dev , epIn , epOut = connect ( UNISOC_VID , UNISOC_PID )
handshake ()
def reconnect ():
global dev
if dev is not None :
usb . util . dispose_resources ( dev )
time . sleep ( 0.5 )
dev , epIn , epOut = connect ( UNISOC_VID , UNISOC_PID )
print ( 'Boot mode entered' )
2021-12-21 12:31:37 +02:00
print ( 'Sending ' + fdl1Label )
2021-12-18 14:20:30 +02:00
send_file_to_addr ( fdl1Name , fdl1Addr )
2021-12-21 12:31:37 +02:00
print ( 'Starting ' + fdl1Label )
2021-12-18 14:20:30 +02:00
resp = reqresp ( unicmd . cmd_data_exec ( fdl1Addr ))
rcode , rlen , r = unicmd . resp_decode ( resp , False )
if rcode == unicmd . BSL_REP_ACK :
2021-12-21 12:31:37 +02:00
print ( fdl1Label + ' started successfully, reconnecting...' )
2021-12-18 14:20:30 +02:00
reconnect ()
handshake ( True )
2021-12-21 12:31:37 +02:00
if singleFdlMode :
rcode = unicmd . BSL_REP_ACK
else :
print ( 'Protocol set up, sending FDL2' )
send_file_to_addr ( fdl2Name , fdl2Addr , True )
print ( 'Starting FDL2' )
resp = reqresp ( unicmd . cmd_data_exec ( fdl2Addr ), True )
rcode , rlen , r = unicmd . resp_decode ( resp , True )
2021-12-18 14:20:30 +02:00
if rcode == unicmd . BSL_REP_ACK :
2021-12-21 12:31:37 +02:00
print ( fdl2Label + ' started successfully!' )
2021-12-18 14:20:30 +02:00
resp = reqresp ( unicmd . cmd_sync_full (), True )
rcode , rlen , r = unicmd . resp_decode ( resp , True )
assert rcode == unicmd . BSL_REP_ACK , 'Could not set the baudrate, response code is %X ' % rcode
2021-12-21 12:31:37 +02:00
print ( fdl2Label + ' running, may start interacting with flash memory' )
2021-12-18 14:20:30 +02:00
2021-12-20 01:16:41 +02:00
if is_flash :
print ( 'Writing flash at offset 0x %X from %s ...' % ( readoffset , outfile ))
2021-12-20 16:35:51 +02:00
write_flash_mem ( outfile , readoffset , readbs , forceErase )
2021-12-20 01:16:41 +02:00
print ( 'Flash memory written, disconnect the device!' )
else :
read_partition ( partitionId , readlen , readoffset , outfile , readbs )
resp = reqresp ( unicmd . cmd_reset (), True )
rcode , rlen , r = unicmd . resp_decode ( resp , True )
assert rcode == unicmd . BSL_REP_ACK , 'Could not reset the device, response code is %X ' % rcode
2021-12-18 14:20:30 +02:00
if dev is not None :
usb . util . dispose_resources ( dev )