Skip to content

Commit 19a6047

Browse files
author
Bjoern Kerler
committed
Support nand firehose. Improve flash info and detection
1 parent 4f47636 commit 19a6047

File tree

4 files changed

+186
-55
lines changed

4 files changed

+186
-55
lines changed

Config/qualcomm_config.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -325,8 +325,8 @@
325325
"MSM8956": secgen[5],
326326
"MSM8976": secgen[5],
327327
"MSM9206": secgen[5],
328-
"MSM9207": secgen[5],
329-
"MSM9607": secgen[5],
328+
"MDM9207": secgen[5],
329+
"MDM9607": secgen[5],
330330
"MDM9x50": secgen[5],
331331
"MDM9x55": secgen[5],
332332
"MDM9x60": secgen[5],
@@ -465,8 +465,8 @@ class memory_type:
465465
"MSM8956": emmc,
466466
"MSM8976": emmc,
467467
"MSM9206": emmc,
468-
"MSM9207": emmc,
469-
"MSM9607": emmc,
468+
"MDM9207": nand,
469+
"MDM9607": nand,
470470
"MDM9x50": emmc,
471471
"MDM9x55": emmc,
472472
"MDM9x60": emmc,
@@ -592,9 +592,9 @@ class memory_type:
592592
"MSM8998_SDM835": 0x00780350,
593593
"MDM9205": 0x000a0320,
594594
"MDM9206_MDM9207tx": 0x000a01d0,
595-
"MDM9207": 0x000a01d0,
596595
"MDM9250": 0x000a01d0,
597596
"MDM9350": 0x000a01d0,
597+
"MDM9207": 0x000a01d0,
598598
"MDM9607": 0x000a01d0,
599599
"MDM9x25": 0xFC4B6028,
600600
"MDM9x30": 0xFC4B6028,

Library/firehose.py

+171-40
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import binascii
22
import platform
33
import time
4+
import json
5+
from struct import unpack
46
from Library.utils import *
57
from Library.gpt import gpt
68
from Library.sparse import QCSparse
@@ -14,6 +16,67 @@
1416
from threading import Thread
1517

1618

19+
class nand_partition:
20+
partentries = []
21+
22+
def __init__(self,parent,printer=None):
23+
if printer==None:
24+
self.printer=print
25+
else:
26+
self.printer=printer
27+
self.partentries=[]
28+
self.partitiontblsector=None
29+
self.parent=parent
30+
self.storage_info = {}
31+
32+
def parse(self,partdata):
33+
self.partentries = []
34+
35+
class partf:
36+
sector = 0
37+
sectors = 0
38+
name = ""
39+
attr1=0
40+
attr2=0
41+
attr3=0
42+
which_flash=0
43+
44+
magic1, magic2, version, numparts = unpack("<IIII", partdata[0:0x10])
45+
if magic1 == 0x55EE73AA or magic2 == 0xE35EBDDB:
46+
data = partdata[0x10:]
47+
for i in range(0, len(data) // 0x1C):
48+
name, offset, length, attr1, attr2, attr3, which_flash = unpack("16sIIBBBB",
49+
data[i * 0x1C:(i * 0x1C) + 0x1C])
50+
if name[1] != 0x3A:
51+
break
52+
np=partf()
53+
np.name=name[2:].rstrip(b"\x00").decode('utf-8').lower()
54+
np.sector=offset*self.parent.cfg.block_size//self.parent.cfg.SECTOR_SIZE_IN_BYTES
55+
np.sectors=(length&0xFFFF)*self.parent.cfg.block_size//self.parent.cfg.SECTOR_SIZE_IN_BYTES
56+
np.attr1=attr1
57+
np.attr2=attr2
58+
np.attr3=attr3
59+
np.which_flash=which_flash
60+
self.partentries.append(np)
61+
return True
62+
return False
63+
64+
def print(self):
65+
self.printer("Name Offset\t\tLength\t\tAttr\t\t\tFlash")
66+
self.printer("-------------------------------------------------------------")
67+
for partition in self.partentries:
68+
name=partition.name
69+
for i in range(0x10 - len(partition.name)):
70+
name += " "
71+
offset = partition.sector * self.parent.cfg.SECTOR_SIZE_IN_BYTES
72+
length = partition.sectors * self.parent.cfg.SECTOR_SIZE_IN_BYTES
73+
attr1 = partition.attr1
74+
attr2 = partition.attr2
75+
attr3 = partition.attr3
76+
which_flash = partition.which_flash
77+
self.printer(
78+
f"{name}\t%08X\t%08X\t{hex(attr1)}/{hex(attr2)}/{hex(attr3)}\t{which_flash}" % (offset, length))
79+
1780
def writefile(wf, q, stop):
1881
while True:
1982
data = q.get()
@@ -42,18 +105,23 @@ def stop(self):
42105

43106
class firehose(metaclass=LogBase):
44107
class cfg:
45-
MemoryName = "eMMC"
46108
TargetName = ""
47109
Version = ""
48110
ZLPAwareHost = 1
49111
SkipStorageInit = 0
50112
SkipWrite = 0
51113
MaxPayloadSizeToTargetInBytes = 1048576
52114
MaxPayloadSizeFromTargetInBytes = 8192
53-
SECTOR_SIZE_IN_BYTES = 512
54115
MaxXMLSizeInBytes = 4096
55116
bit64 = True
56117

118+
total_blocks = 0
119+
block_size = 0
120+
SECTOR_SIZE_IN_BYTES = 0
121+
MemoryName = "eMMC"
122+
prod_name = "Unknown"
123+
maxlun=99
124+
57125
def __init__(self, cdc, xml, cfg, loglevel, devicemodel, serial, skipresponse, luns, args):
58126
self.cdc = cdc
59127
self.lasterror = b""
@@ -67,14 +135,14 @@ def __init__(self, cdc, xml, cfg, loglevel, devicemodel, serial, skipresponse, l
67135
self.skipresponse = skipresponse
68136
self.luns = luns
69137
self.supported_functions = []
70-
if self.cfg.MemoryName == "UFS" or self.cfg.MemoryName == "spinor":
71-
self.cfg.SECTOR_SIZE_IN_BYTES = 4096
138+
72139
self.__logger.setLevel(loglevel)
73140
if loglevel==logging.DEBUG:
74141
logfilename = "log.txt"
75142
fh = logging.FileHandler(logfilename)
76143
self.__logger.addHandler(fh)
77-
144+
self.nandparttbl = None
145+
self.nandpart=nand_partition(parent=self,printer=print)
78146

79147
def detect_partition(self, arguments, partitionname):
80148
fpartitions = {}
@@ -618,29 +686,46 @@ def get_gpt(self, lun, gpt_num_part_entries, gpt_part_entry_size, gpt_part_entry
618686

619687
if data == b"" or data == -1:
620688
return None, None
621-
guid_gpt = gpt(
622-
num_part_entries=gpt_num_part_entries,
623-
part_entry_size=gpt_part_entry_size,
624-
part_entry_start_lba=gpt_part_entry_start_lba,
625-
loglevel=self.__logger.level
626-
)
627-
try:
628-
header = guid_gpt.parseheader(data, self.cfg.SECTOR_SIZE_IN_BYTES)
629-
if "first_usable_lba" in header:
630-
sectors = header["first_usable_lba"]
631-
if sectors == 0:
632-
return None, None
633-
if sectors>34:
634-
sectors=34
635-
data = self.cmd_read_buffer(lun, 0, sectors, False)
636-
if data == b"":
689+
magic=unpack("<I",data[0:4])[0]
690+
if magic==0x844bdcd1:
691+
self.__logger.info("Nand storage detected. Trying to find partition table")
692+
693+
if self.nandpart.partitiontblsector==None:
694+
for sector in range(0, 1024):
695+
data = self.cmd_read_buffer(0,sector,1,False)
696+
if data[0:8] != b"\xac\x9f\x56\xfe\x7a\x12\x7f\xcd":
697+
continue
698+
self.nandpart.partitiontblsector=sector
699+
700+
if self.nandpart.partitiontblsector!=None:
701+
data = self.cmd_read_buffer(0,self.nandpart.partitiontblsector+1, 2, False)
702+
if self.nandpart.parse(data):
703+
return data, self.nandpart
704+
return None, None
705+
else:
706+
guid_gpt = gpt(
707+
num_part_entries=gpt_num_part_entries,
708+
part_entry_size=gpt_part_entry_size,
709+
part_entry_start_lba=gpt_part_entry_start_lba,
710+
loglevel=self.__logger.level
711+
)
712+
try:
713+
header = guid_gpt.parseheader(data, self.cfg.SECTOR_SIZE_IN_BYTES)
714+
if "first_usable_lba" in header:
715+
sectors = header["first_usable_lba"]
716+
if sectors == 0:
717+
return None, None
718+
if sectors>34:
719+
sectors=34
720+
data = self.cmd_read_buffer(lun, 0, sectors, False)
721+
if data == b"":
722+
return None, None
723+
guid_gpt.parse(data, self.cfg.SECTOR_SIZE_IN_BYTES)
724+
return data, guid_gpt
725+
else:
637726
return None, None
638-
guid_gpt.parse(data, self.cfg.SECTOR_SIZE_IN_BYTES)
639-
return data, guid_gpt
640-
else:
727+
except:
641728
return None, None
642-
except:
643-
return None, None
644729

645730
def get_backup_gpt(self, lun, gpt_num_part_entries, gpt_part_entry_size, gpt_part_entry_start_lba):
646731
data = self.cmd_read_buffer(lun, 0, 2, False)
@@ -668,6 +753,12 @@ def calc_offset(self, sector, offset):
668753
return sector, offset
669754

670755
def configure(self,lvl):
756+
if self.cfg.SECTOR_SIZE_IN_BYTES==0:
757+
if self.cfg.MemoryName.lower() == "emmc":
758+
self.cfg.SECTOR_SIZE_IN_BYTES = 512
759+
else:
760+
self.cfg.SECTOR_SIZE_IN_BYTES = 4096
761+
671762
connectcmd = f"<?xml version =\"1.0\" ?><data>" + \
672763
f"<configure MemoryName=\"{self.cfg.MemoryName}\" " + \
673764
f"ZLPAwareHost=\"{str(self.cfg.ZLPAwareHost)}\" " + \
@@ -751,17 +842,17 @@ def configure(self,lvl):
751842
self.__logger.info(f"TargetName={self.cfg.TargetName}")
752843
self.__logger.info(f"MemoryName={self.cfg.MemoryName}")
753844
self.__logger.info(f"Version={self.cfg.Version}")
754-
if self.cfg.MemoryName.lower() == "emmc" or self.cfg.MemoryName.lower()=="nand":
755-
self.cfg.SECTOR_SIZE_IN_BYTES = 512
756-
elif self.cfg.MemoryName.lower() == "ufs" or self.cfg.MemoryName.lower() == "spinor":
757-
self.cfg.SECTOR_SIZE_IN_BYTES = 4096
758845

759846
rsp=self.cmd_read_buffer(0,1,1,False)
760847
if rsp==-1:
761848
if b"ERROR: Failed to initialize (open whole lun) UFS Device slot" in self.lasterror:
762849
self.__logger.warning("Memory type UFS doesn't seem to match (Failed to init). Trying to use eMMC instead.")
763850
self.cfg.MemoryName="eMMC"
764851
return self.configure(0)
852+
elif b"Attribute \'SECTOR_SIZE_IN_BYTES\'=4096 must be equal to disk sector size 512" in self.lasterror:
853+
self.cfg.SECTOR_SIZE_IN_BYTES = 512
854+
elif b"Attribute \'SECTOR_SIZE_IN_BYTES\'=512 must be equal to disk sector size 4096" in self.lasterror:
855+
self.cfg.SECTOR_SIZE_IN_BYTES = 4096
765856

766857
def connect(self):
767858
v = b'-1'
@@ -824,6 +915,45 @@ def connect(self):
824915
self.__logger.info(data.decode('utf-8'))
825916
except:
826917
pass
918+
919+
if self.supported_functions==[]:
920+
self.supported_functions = ['configure', 'program', 'firmwarewrite', 'patch', 'setbootablestoragedrive',
921+
'ufs', 'emmc', 'power', 'benchmark', 'read', 'getstorageinfo',
922+
'getcrc16digest', 'getsha256digest', 'erase', 'peek', 'poke', 'nop', 'xml']
923+
924+
if "getstorageinfo" in self.supported_functions:
925+
storageinfo=self.cmd_getstorageinfo()
926+
if storageinfo!=None:
927+
for info in storageinfo:
928+
if "storage_info" in info:
929+
si=json.loads(info)["storage_info"]
930+
self.__logger.info("Storage report:")
931+
for sii in si:
932+
self.__logger.info(f"{sii}:{si[sii]}")
933+
if "total_blocks" in si:
934+
self.cfg.total_blocks=si["total_blocks"]
935+
936+
if "block_size" in si:
937+
self.cfg.block_size=si["block_size"]
938+
if "page_size" in si:
939+
self.cfg.SECTOR_SIZE_IN_BYTES=si["page_size"]
940+
if "mem_type" in si:
941+
self.cfg.MemoryName=si["mem_type"]
942+
if "prod_name" in si:
943+
self.cfg.prod_name=si["prod_name"]
944+
if "UFS Inquiry Command Output:" in info:
945+
self.cfg.prod_name=info.split("Output: ")[1]
946+
self.__logger.info(info)
947+
if "UFS Erase Block Size:" in info:
948+
self.cfg.block_size=int(info.split("Size: ")[1],16)
949+
self.__logger.info(info)
950+
if "UFS Boot" in info:
951+
self.cfg.MemoryName="UFS"
952+
self.cfg.SECTOR_SIZE_IN_BYTES=4096
953+
if "UFS Boot Partition Enabled: " in info:
954+
self.__logger.info(info)
955+
if "UFS Total Active LU: " in info:
956+
self.cfg.maxlun=int(info.split("LU: ")[1],16)
827957
return self.supported_functions
828958

829959
# OEM Stuff here below --------------------------------------------------
@@ -845,23 +975,24 @@ def cmd_getstorageinfo(self):
845975
data = "<?xml version=\"1.0\" ?><data><getstorageinfo /></data>"
846976
val = self.xmlsend(data)
847977
if val[0]:
848-
self.__logger.info(f"GetStorageInfo:\n--------------------\n")
849-
self.__logger.info(val[1])
850-
return True
978+
data=self.xml.getlog(val[2])
979+
return data
851980
else:
852981
self.__logger.warning("GetStorageInfo command isn't supported.")
853-
return False
982+
return None
854983

855984
def cmd_getstorageinfo_string(self):
856985
data = "<?xml version=\"1.0\" ?><data><getstorageinfo /></data>"
857986
val = self.xmlsend(data)
858-
resp = ""
859-
if val[0] == True:
860-
resp += (f"GetStorageInfo:\n--------------------\n")
861-
resp += (val[1])
862-
return resp
987+
if val[0]:
988+
self.__logger.info(f"GetStorageInfo:\n--------------------\n")
989+
data=self.xml.getlog(val[2])
990+
for line in data:
991+
self.__logger.info(line)
992+
return True
863993
else:
864-
return ""
994+
self.__logger.warning("GetStorageInfo command isn't supported.")
995+
return False
865996

866997
def cmd_poke(self, address, data, filename="", info=False):
867998
rf = None

Library/firehose_client.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,8 @@ def __init__(self, arguments, cdc, sahara, loglevel, printer):
7777

7878
# We assume ufs is fine (hopefully), set it as default
7979
if self.cfg.MemoryName=="":
80-
self.__logger.info("No --memory option set, we assume \"UFS\" as default ..., if it fails, try using \"--memory=eMMC\" instead !")
81-
self.cfg.MemoryName="UFS"
80+
self.__logger.info("No --memory option set, we assume \"eMMC\" as default ..., if it fails, try using \"--memory\" with \"UFS\",\"NAND\" or \"spinor\" instead !")
81+
self.cfg.MemoryName="eMMC"
8282

8383
if self.firehose.configure(0):
8484
funcs = "Supported functions:\n-----------------\n"
@@ -116,8 +116,8 @@ def getluns(self, argument):
116116
return [int(argument["--lun"])]
117117

118118
luns = []
119-
if not self.cfg.MemoryName == "emmc":
120-
for i in range(0, 99):
119+
if self.cfg.MemoryName.lower() == "ufs" or self.cfg.MemoryName.lower()=="spinor":
120+
for i in range(0, self.cfg.maxlun):
121121
luns.append(i)
122122
else:
123123
luns = [0]
@@ -546,7 +546,7 @@ def handle_firehose(self, cmd, options):
546546
self.__logger.error("getstorageinfo command isn't supported by edl loader")
547547
return False
548548
else:
549-
return self.firehose.cmd_getstorageinfo()
549+
return self.firehose.cmd_getstorageinfo_string()
550550
elif cmd == "w":
551551
if not self.check_param(["<partitionname>", "<filename>"]):
552552
return False

0 commit comments

Comments
 (0)