1
1
import binascii
2
2
import platform
3
3
import time
4
+ import json
5
+ from struct import unpack
4
6
from Library .utils import *
5
7
from Library .gpt import gpt
6
8
from Library .sparse import QCSparse
14
16
from threading import Thread
15
17
16
18
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 \t Length\t \t Attr\t \t \t Flash" )
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
+
17
80
def writefile (wf , q , stop ):
18
81
while True :
19
82
data = q .get ()
@@ -42,18 +105,23 @@ def stop(self):
42
105
43
106
class firehose (metaclass = LogBase ):
44
107
class cfg :
45
- MemoryName = "eMMC"
46
108
TargetName = ""
47
109
Version = ""
48
110
ZLPAwareHost = 1
49
111
SkipStorageInit = 0
50
112
SkipWrite = 0
51
113
MaxPayloadSizeToTargetInBytes = 1048576
52
114
MaxPayloadSizeFromTargetInBytes = 8192
53
- SECTOR_SIZE_IN_BYTES = 512
54
115
MaxXMLSizeInBytes = 4096
55
116
bit64 = True
56
117
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
+
57
125
def __init__ (self , cdc , xml , cfg , loglevel , devicemodel , serial , skipresponse , luns , args ):
58
126
self .cdc = cdc
59
127
self .lasterror = b""
@@ -67,14 +135,14 @@ def __init__(self, cdc, xml, cfg, loglevel, devicemodel, serial, skipresponse, l
67
135
self .skipresponse = skipresponse
68
136
self .luns = luns
69
137
self .supported_functions = []
70
- if self .cfg .MemoryName == "UFS" or self .cfg .MemoryName == "spinor" :
71
- self .cfg .SECTOR_SIZE_IN_BYTES = 4096
138
+
72
139
self .__logger .setLevel (loglevel )
73
140
if loglevel == logging .DEBUG :
74
141
logfilename = "log.txt"
75
142
fh = logging .FileHandler (logfilename )
76
143
self .__logger .addHandler (fh )
77
-
144
+ self .nandparttbl = None
145
+ self .nandpart = nand_partition (parent = self ,printer = print )
78
146
79
147
def detect_partition (self , arguments , partitionname ):
80
148
fpartitions = {}
@@ -618,29 +686,46 @@ def get_gpt(self, lun, gpt_num_part_entries, gpt_part_entry_size, gpt_part_entry
618
686
619
687
if data == b"" or data == - 1 :
620
688
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 :
637
726
return None , None
638
- guid_gpt .parse (data , self .cfg .SECTOR_SIZE_IN_BYTES )
639
- return data , guid_gpt
640
- else :
727
+ except :
641
728
return None , None
642
- except :
643
- return None , None
644
729
645
730
def get_backup_gpt (self , lun , gpt_num_part_entries , gpt_part_entry_size , gpt_part_entry_start_lba ):
646
731
data = self .cmd_read_buffer (lun , 0 , 2 , False )
@@ -668,6 +753,12 @@ def calc_offset(self, sector, offset):
668
753
return sector , offset
669
754
670
755
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
+
671
762
connectcmd = f"<?xml version =\" 1.0\" ?><data>" + \
672
763
f"<configure MemoryName=\" { self .cfg .MemoryName } \" " + \
673
764
f"ZLPAwareHost=\" { str (self .cfg .ZLPAwareHost )} \" " + \
@@ -751,17 +842,17 @@ def configure(self,lvl):
751
842
self .__logger .info (f"TargetName={ self .cfg .TargetName } " )
752
843
self .__logger .info (f"MemoryName={ self .cfg .MemoryName } " )
753
844
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
758
845
759
846
rsp = self .cmd_read_buffer (0 ,1 ,1 ,False )
760
847
if rsp == - 1 :
761
848
if b"ERROR: Failed to initialize (open whole lun) UFS Device slot" in self .lasterror :
762
849
self .__logger .warning ("Memory type UFS doesn't seem to match (Failed to init). Trying to use eMMC instead." )
763
850
self .cfg .MemoryName = "eMMC"
764
851
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
765
856
766
857
def connect (self ):
767
858
v = b'-1'
@@ -824,6 +915,45 @@ def connect(self):
824
915
self .__logger .info (data .decode ('utf-8' ))
825
916
except :
826
917
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 )
827
957
return self .supported_functions
828
958
829
959
# OEM Stuff here below --------------------------------------------------
@@ -845,23 +975,24 @@ def cmd_getstorageinfo(self):
845
975
data = "<?xml version=\" 1.0\" ?><data><getstorageinfo /></data>"
846
976
val = self .xmlsend (data )
847
977
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
851
980
else :
852
981
self .__logger .warning ("GetStorageInfo command isn't supported." )
853
- return False
982
+ return None
854
983
855
984
def cmd_getstorageinfo_string (self ):
856
985
data = "<?xml version=\" 1.0\" ?><data><getstorageinfo /></data>"
857
986
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
863
993
else :
864
- return ""
994
+ self .__logger .warning ("GetStorageInfo command isn't supported." )
995
+ return False
865
996
866
997
def cmd_poke (self , address , data , filename = "" , info = False ):
867
998
rf = None
0 commit comments