nuwriter.py 68 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942
  1. # NOTE: This script is test under Python 3.x
  2. __copyright__ = "Copyright (C) 2020~2021 Nuvoton Technology Corp. All rights reserved"
  3. __version__ = "v0.37"
  4. import os
  5. import sys
  6. import argparse
  7. import json
  8. import crcmod
  9. from Crypto.Cipher import AES
  10. import hashlib
  11. import ecdsa
  12. import binascii
  13. from datetime import datetime
  14. import random
  15. import shutil
  16. from tqdm import tqdm
  17. from xusbcom import XUsbComList
  18. from concurrent.futures import ThreadPoolExecutor, as_completed
  19. from UnpackImage import UnpackImage
  20. from collections import namedtuple
  21. from struct import unpack
  22. import time
  23. import platform
  24. # for debug
  25. import usb.core
  26. import usb.util
  27. ACK = 0x55AA55AA
  28. TRANSFER_SIZE = 4096
  29. MAX_HEADER_IMG = 4
  30. # SPI NOR align for erase/program starting address
  31. SPINOR_ALIGN = 4096
  32. # Storage device type
  33. DEV_DDR_SRAM = 0
  34. DEV_NAND = 1
  35. DEV_SD_EMMC = 2
  36. DEV_SPINOR = 3
  37. DEV_SPINAND = 4
  38. DEV_OTP = 6
  39. DEV_USBH = 7
  40. DEV_UNKNOWN = 0xFF
  41. # For OTP programming
  42. ACT_LOAD = 1
  43. ACT_WRITE = 2
  44. ACT_ERASE = 3
  45. ACT_READ = 4
  46. ACT_MSC = 5
  47. # Command options
  48. OPT_NONE = 0
  49. OPT_SCRUB = 1 # For erase, use with care
  50. OPT_WITHBAD = 1 # For read
  51. OPT_EXECUTE = 2 # For write
  52. OPT_VERIFY = 3 # For write
  53. OPT_UNPACK = 4 # For pack
  54. OPT_RAW = 5 # For write
  55. OPT_EJECT = 6 # For msc
  56. OPT_STUFF = 7 # For stuff pack, output could be used by dd command
  57. OPT_SETINFO = 8 # For set storage info for attach
  58. OPT_CONCAT = 9 # For convert, concatenate at the end of encrypted data file
  59. OPT_SHOWHDR = 10 # For convert. Instead of convert, show header content instead
  60. OPT_NOCRC = 11 # For pack. unpack file without crc32 check
  61. OPT_UNKNOWN = 0xFF # Error
  62. # OPT block definitions
  63. OPT_OTPBLK1 = 0x100
  64. OPT_OTPBLK2 = 0x200
  65. OPT_OTPBLK3 = 0x400
  66. OPT_OTPBLK4 = 0x800
  67. OPT_OTPBLK5 = 0x1000
  68. OPT_OTPBLK6 = 0x2000
  69. OPT_OTPBLK7 = 0x4000
  70. OPT_OTPKEY = 0x8000
  71. # for key lock
  72. OPT_OTPKEY0 = 0x10000
  73. OPT_OTPKEY1 = 0x20000
  74. OPT_OTPKEY2 = 0x40000
  75. OPT_OTPKEY3 = 0x80000
  76. OPT_OTPKEY4 = 0x100000
  77. OPT_OTPKEY5 = 0x200000
  78. # Image type definitions
  79. IMG_DATA = 0
  80. IMG_TFA = 1
  81. IMG_UBOOT = 2
  82. IMG_LINUX = 3
  83. IMG_DDR = 4
  84. IMG_TEE = 5
  85. IMG_DTB = 6
  86. # If attach is a must. maybe better for real chip.
  87. # devices = []
  88. mp_mode = False
  89. WINDOWS_PATH = "C:\\Program Files (x86)\\Nuvoton Tools\\NuWriter\\"
  90. LINUX_PATH = "/usr/share/nuwriter/"
  91. def conv_env(env_file_name, blk_size) -> bytearray:
  92. try:
  93. with open(env_file_name, "r") as env_file:
  94. env_data = env_file.read().splitlines()
  95. except (IOError, OSError) as err:
  96. print(f"Open {env_file_name} failed")
  97. sys.exit(err)
  98. out = bytearray(4) # Reserved for CRC
  99. for lines in env_data:
  100. out += bytes(lines, 'ascii')
  101. out += b'\x00'
  102. out += b'\x00'
  103. out += b'\xFF' * (blk_size - len(out))
  104. crc32_func = crcmod.predefined.mkCrcFun('crc-32')
  105. checksum = crc32_func(out[4:])
  106. out[0:4] = checksum.to_bytes(4, byteorder="little")
  107. return out
  108. def get_dpm(dpm) -> int:
  109. return {
  110. 'a35sdsdis': 0x00000001,
  111. 'a35sdslock': 0x00000002,
  112. 'a35sndsdis': 0x00000004,
  113. 'a35sndslock': 0x00000008,
  114. 'a35nsdsdis': 0x00000010,
  115. 'a35nsdslock': 0x00000020,
  116. 'a35nsndsdis': 0x00000040,
  117. 'a35nsndslock': 0x00000080,
  118. 'm4dsdis': 0x00000100,
  119. 'm4dslock': 0x00000200,
  120. 'm4ndsdis': 0x00000400,
  121. 'm4ndslock': 0x00000800,
  122. 'extdis': 0x00001000,
  123. 'extlock': 0x00002000,
  124. 'exttdis': 0x00004000,
  125. 'exttlock': 0x00008000,
  126. 'giccfgsdis': 0x00010000,
  127. 'giccfgslock': 0x00020000
  128. }.get(dpm, 0)
  129. def get_plm(plm) -> int:
  130. return {
  131. 'oem': 0x1,
  132. 'deploy': 0x3,
  133. 'rma': 0x7,
  134. 'prma': 0xF
  135. }.get(plm, 0)
  136. def conv_otp(opt_file_name) -> (bytearray, int):
  137. try:
  138. with open(opt_file_name, "r") as json_file:
  139. try:
  140. d = json.load(json_file)
  141. except json.decoder.JSONDecodeError as err:
  142. print(f"{opt_file_name} parsing error")
  143. sys.exit(err)
  144. except (IOError, OSError) as err:
  145. print(f"Open {opt_file_name} failed")
  146. sys.exit(err)
  147. # Bootcfg, DPM, PLM, and PWD 4 bytes each, MAC addr 8 bytes each, sec/nsec 88 bytes each
  148. data = bytearray(208)
  149. option = 0
  150. for key in d.keys():
  151. if key == 'boot_cfg':
  152. cfg_val = 0
  153. for sub_key in d['boot_cfg'].keys():
  154. if sub_key == 'posotp':
  155. if d['boot_cfg']['posotp'] == 'enable':
  156. cfg_val |= 1
  157. if sub_key == 'qspiclk':
  158. if d['boot_cfg']['qspiclk'] == '50mhz':
  159. cfg_val |= 2
  160. if sub_key == 'wdt1en':
  161. if d['boot_cfg']['wdt1en'] == 'enable':
  162. cfg_val |= 4
  163. if sub_key == 'uart0en':
  164. if d['boot_cfg']['uart0en'] == 'disable':
  165. cfg_val |= 0x10
  166. if sub_key == 'sd0bken':
  167. if d['boot_cfg']['sd0bken'] == 'enable':
  168. cfg_val |= 0x20
  169. if sub_key == 'tsiimg':
  170. if d['boot_cfg']['tsiimg'] == 'enable':
  171. cfg_val |= 0x40
  172. if sub_key == 'tsidbg':
  173. if d['boot_cfg']['tsidbg'] == 'disable':
  174. cfg_val |= 0x80
  175. if sub_key == 'bootsrc':
  176. if d['boot_cfg']['bootsrc'] == 'sd' or d['boot_cfg']['bootsrc'] == 'emmc':
  177. cfg_val |= 0x400
  178. elif d['boot_cfg']['bootsrc'] == 'nand':
  179. cfg_val |= 0x800
  180. elif d['boot_cfg']['bootsrc'] == 'usb':
  181. cfg_val |= 0xC00
  182. if sub_key == 'page':
  183. if d['boot_cfg']['page'] == '2k':
  184. cfg_val |= 0x1000
  185. elif d['boot_cfg']['page'] == '4k':
  186. cfg_val |= 0x2000
  187. elif d['boot_cfg']['page'] == '8k':
  188. cfg_val |= 0x3000
  189. if sub_key == 'option':
  190. if d['boot_cfg']['option'] == 'sd1' or d['boot_cfg']['option'] == 'emmc1' or \
  191. d['boot_cfg']['option'] == 't12' or d['boot_cfg']['option'] == 'spinand4':
  192. cfg_val |= 0x4000
  193. elif d['boot_cfg']['option'] == 't24' or d['boot_cfg']['option'] == 'spinor1':
  194. cfg_val |= 0x8000
  195. elif d['boot_cfg']['option'] == 'noecc' or d['boot_cfg']['option'] == 'spinor4':
  196. cfg_val |= 0xC000
  197. if sub_key == 'secboot':
  198. if d['boot_cfg']['secboot'] == 'disable':
  199. cfg_val |= 0x5A000000
  200. data[0:4] = cfg_val.to_bytes(4, byteorder='little')
  201. option |= OPT_OTPBLK1
  202. elif key == 'dpm_plm':
  203. for sub_key in d['dpm_plm'].keys():
  204. if sub_key == 'dpm':
  205. dpm_val = 0
  206. for dpm_key in d['dpm_plm']['dpm'].keys():
  207. dpm_val |= get_dpm(dpm_key)
  208. if dpm_val != 0:
  209. data[4:8] = dpm_val.to_bytes(4, byteorder='little')
  210. elif sub_key == 'plm':
  211. plm_val = get_plm(d['dpm_plm']['plm'])
  212. if plm_val != 0:
  213. data[8:12] = plm_val.to_bytes(4, byteorder='little')
  214. option |= OPT_OTPBLK2
  215. elif key == 'mac0':
  216. data[12:18] = bytes.fromhex(d['mac0'])
  217. option |= OPT_OTPBLK3
  218. elif key == 'mac1':
  219. data[20:26] = bytes.fromhex(d['mac1'])
  220. option |= OPT_OTPBLK4
  221. elif key == 'dplypwd':
  222. data[28:32] = bytes.fromhex(d['dplypwd'])
  223. option |= OPT_OTPBLK5
  224. elif key == 'sec':
  225. newkey = bytes.fromhex(d['sec'])
  226. newkey += b'\x00' * (88 - len(newkey))
  227. data[32:120] = newkey
  228. option |= OPT_OTPBLK6
  229. elif key == 'nonsec':
  230. newkey = bytes.fromhex(d['nonsec'])
  231. newkey += b'\x00' * (88 - len(newkey))
  232. data[120:208] = newkey
  233. option |= OPT_OTPBLK7
  234. elif key == 'huk0':
  235. newkey = bytes.fromhex(d['huk0']['key'])
  236. if len(newkey) != 16:
  237. print("HUK0 is 128-bit")
  238. sys.exit(2)
  239. newkey += b'\x00' * (32 - len(newkey))
  240. # size - 128-bit
  241. newkey += b'\x08\x00\x00\x00'
  242. # key number - 0
  243. newkey += b'\x00\x00\x00\x00'
  244. # meta - owner: cpu, cpu readable
  245. newkey += b'\x04\x00\x05\x80'
  246. data += newkey
  247. elif key == 'huk1':
  248. newkey = bytes.fromhex(d['huk1']['key'])
  249. if len(newkey) != 16:
  250. print("HUK1 is 128-bit")
  251. sys.exit(2)
  252. newkey += b'\x00' * (32 - len(newkey))
  253. # size - 128-bit
  254. newkey += b'\x08\x00\x00\x00'
  255. # key number - 1
  256. newkey += b'\x01\x00\x00\x00'
  257. # meta - owner: cpu, cpu readable
  258. newkey += b'\x04\x00\x05\x80'
  259. data += newkey
  260. elif key == 'huk2':
  261. newkey = bytes.fromhex(d['huk2']['key'])
  262. if len(newkey) != 16:
  263. print("HUK0 is 128-bit")
  264. sys.exit(2)
  265. newkey += b'\x00' * (32 - len(newkey))
  266. # size - 128-bit
  267. newkey += b'\x08\x00\x00\x00'
  268. # key number - 2
  269. newkey += b'\x02\x00\x00\x00'
  270. # meta - owner: cpu, cpu readable
  271. newkey += b'\x04\x00\x05\x80'
  272. data += newkey
  273. elif key == 'key3':
  274. newkey = bytes.fromhex(d['key3']['key'])
  275. if len(newkey) != 32:
  276. print("key3 is 256-bit")
  277. sys.exit(2)
  278. newkey += b'\x00' * (32 - len(newkey))
  279. # size - 256-bit
  280. newkey += b'\x00\x01\x00\x00'
  281. # key number - 3
  282. newkey += b'\x03\x00\x00\x00'
  283. if d['key3']['meta'] == 'aes256-unreadable':
  284. newkey += b'\x00\x06\x00\x80'
  285. elif d['key3']['meta'] == 'aes256-cpu-readable':
  286. newkey += b'\x04\x06\x00\x80'
  287. elif d['key3']['meta'] == 'sha256-unreadable':
  288. newkey += b'\x00\x06\x01\x80'
  289. elif d['key3']['meta'] == 'sha256-cpu-readable':
  290. newkey += b'\x04\x06\x01\x80'
  291. elif d['key3']['meta'] == 'eccp256-unreadable':
  292. newkey += b'\x00\x06\x04\x80'
  293. elif d['key3']['meta'] == 'eccp256-cpu-readable':
  294. newkey += b'\x04\x06\x04\x80'
  295. data += newkey
  296. elif key == 'key4':
  297. newkey = bytes.fromhex(d['key4']['key'])
  298. if len(newkey) != 32:
  299. print("key4 is 256-bit")
  300. sys.exit(2)
  301. newkey += b'\x00' * (32 - len(newkey))
  302. # size - 256-bit
  303. newkey += b'\x00\x01\x00\x00'
  304. # key number - 4
  305. newkey += b'\x04\x00\x00\x00'
  306. if d['key4']['meta'] == 'aes256-unreadable':
  307. newkey += b'\x00\x06\x00\x80'
  308. elif d['key4']['meta'] == 'aes256-cpu-readable':
  309. newkey += b'\x04\x06\x00\x80'
  310. elif d['key4']['meta'] == 'sha256-unreadable':
  311. newkey += b'\x00\x06\x01\x80'
  312. elif d['key4']['meta'] == 'sha256-cpu-readable':
  313. newkey += b'\x04\x06\x01\x80'
  314. elif d['key4']['meta'] == 'eccp256-unreadable':
  315. newkey += b'\x00\x06\x04\x80'
  316. elif d['key4']['meta'] == 'eccp256-cpu-readable':
  317. newkey += b'\x04\x06\x04\x80'
  318. data += newkey
  319. elif key == 'key5':
  320. newkey = bytes.fromhex(d['key5']['key'])
  321. if len(newkey) != 32:
  322. print("key5 is 256-bit")
  323. sys.exit(2)
  324. newkey += b'\x00' * (32 - len(newkey))
  325. # size - 256-bit
  326. newkey += b'\x00\x01\x00\x00'
  327. # key number - 5
  328. newkey += b'\x05\x00\x00\x00'
  329. if d['key5']['meta'] == 'aes256-unreadable':
  330. newkey += b'\x00\x06\x00\x80'
  331. elif d['key5']['meta'] == 'aes256-cpu-readable':
  332. newkey += b'\x04\x06\x00\x80'
  333. elif d['key5']['meta'] == 'sha256-unreadable':
  334. newkey += b'\x00\x06\x01\x80'
  335. elif d['key5']['meta'] == 'sha256-cpu-readable':
  336. newkey += b'\x04\x06\x01\x80'
  337. elif d['key5']['meta'] == 'eccp256-unreadable':
  338. newkey += b'\x00\x06\x04\x80'
  339. elif d['key5']['meta'] == 'eccp256-cpu-readable':
  340. newkey += b'\x04\x06\x04\x80'
  341. data += newkey
  342. elif key == 'publicx':
  343. newkey = bytes.fromhex(d['publicx'])
  344. if len(newkey) != 32:
  345. print("IBR publicx is 256-bit")
  346. sys.exit(2)
  347. data += bytes.fromhex(d['publicx'])
  348. data += b'\x00\x01\x00\x00' # 256 bits
  349. data += b'\x06\x00\x00\x00'
  350. data += b'\x01\x06\x04\x80'
  351. elif key == 'publicy':
  352. newkey = bytes.fromhex(d['publicy'])
  353. if len(newkey) != 32:
  354. print("IBR publicy is 256-bit")
  355. sys.exit(2)
  356. data += bytes.fromhex(d['publicy'])
  357. data += b'\x00\x01\x00\x00' # 256 bits
  358. data += b'\x07\x00\x00\x00'
  359. data += b'\x01\x06\x04\x80'
  360. elif key == 'aeskey':
  361. newkey = bytes.fromhex(d['aeskey'])
  362. if len(newkey) != 32:
  363. print("IBR aeskey is 256-bit")
  364. sys.exit(2)
  365. data += bytes.fromhex(d['aeskey'])
  366. data += b'\x00\x01\x00\x00' # 256 bits
  367. data += b'\x08\x00\x00\x00'
  368. data += b'\x01\x06\x00\x80'
  369. try:
  370. with open("otp_data.bin", "wb") as out_file:
  371. out_file.write(data[0:len(data)])
  372. except (IOError, OSError) as err:
  373. print(f"Open otp_data.bin failed")
  374. sys.exit(err)
  375. if len(data) > 208:
  376. option |= OPT_OTPKEY
  377. return data, option
  378. def __img_erase(dev, media, start, length, option) -> int:
  379. nand_align, spinand_align = dev.get_align()
  380. if (media == DEV_NAND and nand_align == 0) or \
  381. (media == DEV_SPINAND and spinand_align == 0):
  382. print("Unable to get block size")
  383. return -1
  384. if (media == DEV_NAND and start % nand_align != 0) or\
  385. (media == DEV_SPINAND and start % spinand_align != 0) or \
  386. (media == DEV_SPINOR and start % SPINOR_ALIGN != 0):
  387. print("Starting address must be block aligned")
  388. return -1
  389. cmd = start.to_bytes(8, byteorder='little')
  390. cmd += length.to_bytes(8, byteorder='little')
  391. cmd += ACT_ERASE.to_bytes(4, byteorder='little')
  392. cmd += option.to_bytes(4, byteorder='little')
  393. dev.set_media(media)
  394. dev.write(cmd)
  395. ack = dev.read(4)
  396. if int.from_bytes(ack, byteorder="little") != ACK:
  397. print("Receive ACK error")
  398. return -1
  399. bar = tqdm(total=100, position=dev.get_id(), ascii=True)
  400. previous_progress = 0
  401. while True:
  402. # xusb ack with total erase progress.
  403. ack = dev.read(4)
  404. if int.from_bytes(ack, byteorder="little") <= 100:
  405. bar.update(int.from_bytes(ack, byteorder="little") - previous_progress)
  406. previous_progress = int.from_bytes(ack, byteorder="little")
  407. if int.from_bytes(ack, byteorder="little") == 100:
  408. break
  409. bar.close()
  410. return 0
  411. # default erase all (count=0)
  412. def do_img_erase(media, start, length=0, option=OPT_NONE) -> None:
  413. global mp_mode
  414. # devices = XUsbComList(attach_all=mp_mode).get_dev()
  415. _XUsbComList = XUsbComList(attach_all=mp_mode)
  416. devices = _XUsbComList.get_dev()
  417. if len(devices) == 0:
  418. print("Device not found")
  419. sys.exit(2)
  420. with ThreadPoolExecutor(max_workers=8) as executor:
  421. futures = [executor.submit(__img_erase, dev, media, start, length, option) for dev in devices]
  422. success = 0
  423. failed = 0
  424. for future in as_completed(futures):
  425. if future.result() == 0:
  426. success += 1
  427. else:
  428. failed += 1
  429. print(f"Successfully erased {success} device(s)")
  430. if failed > 0:
  431. print(f"Failed to erase {failed} device(s)")
  432. def do_otp_erase(option) -> None:
  433. global mp_mode
  434. # devices = XUsbComList(attach_all=mp_mode).get_dev()
  435. _XUsbComList = XUsbComList(attach_all=mp_mode)
  436. devices = _XUsbComList.get_dev()
  437. if len(devices) == 0:
  438. print("Device not found")
  439. sys.exit(2)
  440. start = 0
  441. length = 0
  442. dev = devices[0]
  443. load_otp_writer(dev)
  444. cmd = start.to_bytes(8, byteorder='little')
  445. cmd += length.to_bytes(8, byteorder='little')
  446. cmd += ACT_ERASE.to_bytes(4, byteorder='little')
  447. cmd += option.to_bytes(4, byteorder='little')
  448. dev.set_media(DEV_OTP)
  449. dev.write(cmd)
  450. ack = dev.read(4)
  451. if int.from_bytes(ack, byteorder="little") != ACK:
  452. print("Receive ACK error")
  453. print(f"Failed to erase device(s)")
  454. # There's no way to tell the progress...
  455. ack = dev.read(4)
  456. data = int.from_bytes(ack, byteorder="little")
  457. if option == 0x100:
  458. data >>= 2
  459. data &= 0x3
  460. if option == 0x400:
  461. data >>= 6
  462. data &= 0x3
  463. if option == 0x800:
  464. data >>= 8
  465. data &= 0x3
  466. if option & 0x8000:
  467. data >>= 16
  468. #print(f"Erase count state {hex(data)}")
  469. print(f"Successfully erased device(s)")
  470. def load_otp_writer(dev) -> int:
  471. try:
  472. with open("otp_writer.bin", "rb") as writer_file:
  473. otp_writer = writer_file.read()
  474. except (IOError, OSError) as err:
  475. print(f"Open {opt_file_name} failed")
  476. sys.exit(err)
  477. option = 0
  478. img_length = len(otp_writer)
  479. cmd = b'\x00\x00\xf0\x86\x00\x00\x00\x00'
  480. cmd += img_length.to_bytes(8, byteorder='little')
  481. cmd += ACT_LOAD.to_bytes(4, byteorder='little')
  482. cmd += option.to_bytes(4, byteorder='little')
  483. dev.set_media(DEV_OTP)
  484. dev.write(cmd)
  485. ack = dev.read(4)
  486. if int.from_bytes(ack, byteorder="little") != ACK:
  487. print("Receive ACK error")
  488. return -1
  489. for offset in range(0, img_length, TRANSFER_SIZE):
  490. xfer_size = TRANSFER_SIZE if offset + TRANSFER_SIZE < img_length else img_length - offset
  491. dev.write(otp_writer[offset: offset + xfer_size])
  492. ack = dev.read(4)
  493. if int.from_bytes(ack, byteorder="little") != xfer_size:
  494. print("Acked size error")
  495. return -1
  496. while True:
  497. # wait TSI update firmware
  498. ack = dev.read(4)
  499. if int.from_bytes(ack, byteorder="little") == ACK:
  500. break
  501. return 0
  502. def __otp_program(dev, otp_data, option) -> int:
  503. img_length = len(otp_data)
  504. cmd = b'\x00\x00\xf0\x86\x00\x00\x00\x00'
  505. cmd += img_length.to_bytes(8, byteorder='little')
  506. cmd += ACT_WRITE.to_bytes(4, byteorder='little')
  507. cmd += option.to_bytes(4, byteorder='little')
  508. dev.set_media(DEV_OTP)
  509. dev.write(cmd)
  510. ack = dev.read(4)
  511. if int.from_bytes(ack, byteorder="little") != ACK:
  512. print("Receive ACK error")
  513. return -1
  514. # There's no way to tell the progress...
  515. dev.write(otp_data)
  516. ack = dev.read(4)
  517. if int.from_bytes(ack, byteorder="little") != img_length:
  518. print("Acked size error")
  519. return -1
  520. # There's no way to tell the progress...
  521. ack = dev.read(4)
  522. data = int.from_bytes(ack, byteorder="little")
  523. #print(f"Can program count {hex(data)}")
  524. return 0
  525. def do_otp_program(opt_file_name) -> None:
  526. global mp_mode
  527. # devices = XUsbComList(attach_all=mp_mode).get_dev()
  528. _XUsbComList = XUsbComList(attach_all=mp_mode)
  529. devices = _XUsbComList.get_dev()
  530. if len(devices) == 0:
  531. print("Device not found")
  532. sys.exit(2)
  533. load_otp_writer(devices[0])
  534. otp_data, option = conv_otp(opt_file_name)
  535. with ThreadPoolExecutor(max_workers=8) as executor:
  536. futures = [executor.submit(__otp_program, dev, otp_data, option) for dev in devices]
  537. success = 0
  538. failed = 0
  539. for future in as_completed(futures):
  540. if future.result() == 0:
  541. success += 1
  542. else:
  543. failed += 1
  544. print(f"Successfully programmed {success} device(s)")
  545. if failed > 0:
  546. print(f"Failed to program {failed} device(s)")
  547. def do_otp_read(media, start, out_file_name, length=0x1, option=OPT_NONE) -> None:
  548. global mp_mode
  549. # devices = XUsbComList(attach_all=mp_mode).get_dev()
  550. _XUsbComList = XUsbComList(attach_all=mp_mode)
  551. devices = _XUsbComList.get_dev()
  552. if len(devices) == 0:
  553. print("Device not found")
  554. sys.exit(2)
  555. # Only support one device in read function
  556. dev = devices[0]
  557. load_otp_writer(dev)
  558. cmd = start.to_bytes(8, byteorder='little')
  559. cmd += length.to_bytes(8, byteorder='little')
  560. cmd += ACT_READ.to_bytes(4, byteorder='little')
  561. cmd += option.to_bytes(4, byteorder='little')
  562. dev.set_media(media)
  563. dev.write(cmd)
  564. ack = dev.read(4)
  565. if int.from_bytes(ack, byteorder="little") != ACK:
  566. print("Receive ACK error")
  567. return
  568. # FIXME: Don't know real length for "read all"
  569. bar = tqdm(total=length, ascii=True)
  570. data = b''
  571. remain = length
  572. while remain > 0:
  573. ack = dev.read(4)
  574. # Get the transfer length of next read
  575. xfer_size = int.from_bytes(ack, byteorder="little")
  576. data += dev.read(xfer_size)
  577. dev.write(xfer_size.to_bytes(4, byteorder='little')) # ack
  578. remain -= xfer_size
  579. bar.update(xfer_size)
  580. try:
  581. with open(out_file_name, "wb") as out_file:
  582. out_file.write(data[0:length])
  583. except (IOError, OSError) as err:
  584. print(f"Open {out_file_name} failed")
  585. sys.exit(err)
  586. bar.close()
  587. def __pack_program(dev, media, pack_image, option) -> int:
  588. nand_align, spinand_align = dev.get_align()
  589. image_cnt = pack_image.img_count()
  590. if (media == DEV_NAND and nand_align == 0) or \
  591. (media == DEV_SPINAND and spinand_align == 0):
  592. print("Unable to get block size")
  593. return -1
  594. for i in range(image_cnt):
  595. img_length, img_start, img_type = pack_image.img_attr(i)
  596. if (media == DEV_NAND and img_start % nand_align != 0) or \
  597. (media == DEV_SPINAND and img_start % spinand_align != 0) or \
  598. (media == DEV_SPINOR and img_start % SPINOR_ALIGN != 0):
  599. print("Starting address must be block aligned")
  600. return -1
  601. time.sleep(1)
  602. dev.set_media(media)
  603. cmd = img_start.to_bytes(8, byteorder='little')
  604. cmd += img_length.to_bytes(8, byteorder='little')
  605. cmd += ACT_WRITE.to_bytes(4, byteorder='little')
  606. cmd += img_type.to_bytes(4, byteorder='little')
  607. dev.write(cmd)
  608. ack = dev.read(4)
  609. if int.from_bytes(ack, byteorder="little") != ACK:
  610. print("Receive ACK error")
  611. return -1
  612. text = f"Programming {i}/{image_cnt}"
  613. bar = tqdm(total=img_length, position=dev.get_id(), ascii=True, desc=text)
  614. for offset in range(0, img_length, TRANSFER_SIZE):
  615. xfer_size = TRANSFER_SIZE if offset + TRANSFER_SIZE < img_length else img_length - offset
  616. dev.write(pack_image.img_content(i, offset, xfer_size))
  617. ack = dev.read(4)
  618. if int.from_bytes(ack, byteorder="little") != xfer_size:
  619. print("Ack size error")
  620. return -1
  621. bar.update(xfer_size)
  622. bar.close()
  623. dev.read(4)
  624. # FIXME: Added time.sleep(1) to make SPI NAND Pack Program + Verify PASS
  625. time.sleep(1)
  626. if option == OPT_VERIFY:
  627. dev.set_media(media)
  628. cmd = img_start.to_bytes(8, byteorder='little')
  629. cmd += img_length.to_bytes(8, byteorder='little')
  630. cmd += ACT_READ.to_bytes(4, byteorder='little')
  631. cmd += b'\x00' * 4
  632. dev.write(cmd)
  633. ack = dev.read(4)
  634. if int.from_bytes(ack, byteorder="little") != ACK:
  635. print("Receive ACK error")
  636. return -1
  637. remain = img_length
  638. text = f"Verifying {i}/{image_cnt}"
  639. bar = tqdm(total=img_length, position=dev.get_id(), ascii=True, desc=text)
  640. while remain > 0:
  641. ack = dev.read(4)
  642. # Get the transfer length of next read
  643. xfer_size = int.from_bytes(ack, byteorder="little")
  644. data = dev.read(xfer_size)
  645. dev.write(xfer_size.to_bytes(4, byteorder='little'))
  646. offset = img_length - remain
  647. # For SD/eMMC
  648. if xfer_size > remain:
  649. xfer_size = remain
  650. data = data[0: remain]
  651. if data != bytearray(pack_image.img_content(i, offset, xfer_size)):
  652. print("Verify failed")
  653. return -1
  654. remain -= xfer_size
  655. bar.update(xfer_size)
  656. bar.close()
  657. return 0
  658. def do_pack_program(media, pack_file_name, option=OPT_NONE) -> None:
  659. global mp_mode
  660. # devices = XUsbComList(attach_all=mp_mode).get_dev()
  661. _XUsbComList = XUsbComList(attach_all=mp_mode)
  662. devices = _XUsbComList.get_dev()
  663. if len(devices) == 0:
  664. print("Device not found")
  665. sys.exit(2)
  666. pack_image = UnpackImage(pack_file_name, option)
  667. with ThreadPoolExecutor(max_workers=8) as executor:
  668. futures = [executor.submit(__pack_program, dev, media, pack_image, option) for dev in devices]
  669. success = 0
  670. failed = 0
  671. for future in as_completed(futures):
  672. if future.result() == 0:
  673. success += 1
  674. else:
  675. failed += 1
  676. print(f"Successfully programmed {success} device(s)")
  677. if failed > 0:
  678. print(f"Failed to program {failed} device(s)")
  679. def __img_program(dev, media, start, img_data, option) -> int:
  680. nand_align, spinand_align = dev.get_align()
  681. if (media == DEV_NAND and nand_align == 0) or \
  682. (media == DEV_SPINAND and spinand_align == 0):
  683. print("Unable to get block size")
  684. return -1
  685. if (media == DEV_NAND and start % nand_align != 0) or\
  686. (media == DEV_SPINAND and start % spinand_align != 0) or \
  687. (media == DEV_SPINOR and start % SPINOR_ALIGN != 0):
  688. print("Starting address must be block aligned")
  689. return -1
  690. img_length = len(img_data)
  691. print(f"image length is {img_length}")
  692. cmd = start.to_bytes(8, byteorder='little')
  693. cmd += img_length.to_bytes(8, byteorder='little')
  694. cmd += ACT_WRITE.to_bytes(4, byteorder='little')
  695. if option == OPT_EXECUTE:
  696. cmd += option.to_bytes(4, byteorder='little')
  697. else:
  698. cmd += b'\x00' * 4
  699. dev.set_media(media)
  700. dev.write(cmd)
  701. ack = dev.read(4)
  702. if int.from_bytes(ack, byteorder="little") != ACK:
  703. print("Receive ACK error")
  704. return -1
  705. # Set ascii=True is for Windows cmd terminal, position > 0 doesn't work as expected in cmd though...
  706. bar = tqdm(total=img_length, position=dev.get_id(), ascii=True, desc="Programming")
  707. for offset in range(0, img_length, TRANSFER_SIZE):
  708. xfer_size = TRANSFER_SIZE if offset + TRANSFER_SIZE < img_length else img_length - offset
  709. dev.write(img_data[offset: offset + xfer_size])
  710. ack = dev.read(4)
  711. if int.from_bytes(ack, byteorder="little") != xfer_size:
  712. print("Ack size error")
  713. return -1
  714. bar.update(xfer_size)
  715. dev.read(4)
  716. bar.close()
  717. if option == OPT_VERIFY:
  718. dev.set_media(media)
  719. cmd = start.to_bytes(8, byteorder='little')
  720. cmd += img_length.to_bytes(8, byteorder='little')
  721. cmd += ACT_READ.to_bytes(4, byteorder='little')
  722. cmd += b'\x00' * 4
  723. dev.write(cmd)
  724. ack = dev.read(4)
  725. if int.from_bytes(ack, byteorder="little") != ACK:
  726. print("Receive ACK error")
  727. return -1
  728. remain = img_length
  729. bar = tqdm(total=img_length, position=dev.get_id(), ascii=True, desc="Verifying")
  730. while remain > 0:
  731. ack = dev.read(4)
  732. # Get the transfer length of next read
  733. xfer_size = int.from_bytes(ack, byteorder="little")
  734. data = dev.read(xfer_size)
  735. dev.write(xfer_size.to_bytes(4, byteorder='little')) # ack
  736. offset = img_length - remain
  737. # For SD/eMMC
  738. if xfer_size > remain:
  739. xfer_size = remain
  740. data = data[0: remain]
  741. if data != bytearray(img_data[offset: offset + xfer_size]):
  742. print("Verify failed")
  743. return -1
  744. remain -= xfer_size
  745. bar.update(xfer_size)
  746. print("Verify pass")
  747. bar.close()
  748. return 0
  749. def do_img_program(media, start, image_file_name, option=OPT_NONE) -> None:
  750. global mp_mode
  751. # devices = XUsbComList(attach_all=mp_mode).get_dev()
  752. _XUsbComList = XUsbComList(attach_all=mp_mode)
  753. devices = _XUsbComList.get_dev()
  754. if len(devices) == 0:
  755. print("Device not found")
  756. sys.exit(2)
  757. try:
  758. with open(image_file_name, "rb") as image_file:
  759. img_data = image_file.read()
  760. except (IOError, OSError) as err:
  761. print(f"Open {image_file_name} failed")
  762. sys.exit(err)
  763. with ThreadPoolExecutor(max_workers=8) as executor:
  764. futures = [executor.submit(__img_program, dev, media, start, img_data, option) for dev in devices]
  765. success = 0
  766. failed = 0
  767. for future in as_completed(futures):
  768. if future.result() == 0:
  769. success += 1
  770. else:
  771. failed += 1
  772. print(f"Successfully programmed {success} device(s)")
  773. if failed > 0:
  774. print(f"Failed to program {failed} device(s)")
  775. def do_img_read(media, start, out_file_name, length=0x1, option=OPT_NONE) -> None:
  776. # only support read from 1 device
  777. # devices = XUsbComList(attach_all=False).get_dev()
  778. _XUsbComList = XUsbComList(attach_all=False)
  779. devices = _XUsbComList.get_dev()
  780. if len(devices) == 0:
  781. print("Device not found")
  782. sys.exit(2)
  783. # Only support one device in read function
  784. dev = devices[0]
  785. cmd = start.to_bytes(8, byteorder='little')
  786. cmd += length.to_bytes(8, byteorder='little')
  787. cmd += ACT_READ.to_bytes(4, byteorder='little')
  788. cmd += option.to_bytes(4, byteorder='little')
  789. dev.set_media(media)
  790. dev.write(cmd)
  791. ack = dev.read(4)
  792. if int.from_bytes(ack, byteorder="little") != ACK:
  793. print("Receive ACK error")
  794. return
  795. # FIXME: Don't know real length for "read all"
  796. bar = tqdm(total=length, ascii=True)
  797. data = b''
  798. remain = length
  799. while remain > 0:
  800. ack = dev.read(4)
  801. # Get the transfer length of next read
  802. xfer_size = int.from_bytes(ack, byteorder="little")
  803. data += dev.read(xfer_size)
  804. dev.write(xfer_size.to_bytes(4, byteorder='little')) # ack
  805. remain -= xfer_size
  806. bar.update(xfer_size)
  807. try:
  808. with open(out_file_name, "wb") as out_file:
  809. out_file.write(data[0:length])
  810. except (IOError, OSError) as err:
  811. print(f"Open {out_file_name} failed")
  812. sys.exit(err)
  813. bar.close()
  814. def __attach(dev, ini_data, xusb_data) -> int:
  815. ini_len = len(ini_data)
  816. out = int(ini_len).to_bytes(4, byteorder="little")
  817. out += b'\x00\x00\x03\x28' # Execute address is 0x28030000
  818. dev.write(out)
  819. dev.write(ini_data)
  820. in_buf = dev.read(4)
  821. if int.from_bytes(in_buf, byteorder="little") != ini_len:
  822. print("Length error")
  823. return -1
  824. in_buf = dev.read(4)
  825. if int.from_bytes(in_buf, byteorder="little") != ACK:
  826. val = int.from_bytes(in_buf, byteorder="little")
  827. print(f"Ack error {val}")
  828. return -1
  829. xusb_len = len(xusb_data)
  830. out = int(xusb_len).to_bytes(4, byteorder="little")
  831. out += b'\x00\x00\x00\x87' # Execute address is 0x87000000
  832. dev.write(out)
  833. for offset in range(0, xusb_len, TRANSFER_SIZE):
  834. xfer_size = TRANSFER_SIZE if offset + TRANSFER_SIZE < xusb_len else xusb_len - offset
  835. dev.write(xusb_data[offset: offset + xfer_size])
  836. if offset + xfer_size != xusb_len: # Ignore the ack of last packet
  837. ack = dev.read(4)
  838. if int.from_bytes(ack, byteorder="little") != xfer_size:
  839. _ack = int.from_bytes(ack, byteorder="little")
  840. print(f"Ack size error {_ack} {xfer_size}")
  841. return -1
  842. return 0
  843. def __get_info(dev, data) -> int:
  844. try:
  845. info = dev.get_info(data)
  846. except usb.core.USBError as err:
  847. sys.exit(err)
  848. _info_struct = namedtuple('_info_struct',
  849. 'page_per_blk page_size blk_cnt bad_clk_cnt oob_size usr_cfg0 spi_id usr_cfg1 quad_cmd \
  850. read_sts_cmd write_sts_cmd sts_val dummy_byte blk rsv use_cfg2 snand_id snand_page_size \
  851. snand_oob snand_quad_cmd snand_read_sts_cmd snand_write_sts_cmd snand_sts_val \
  852. snand_dummy_byte snand_blk_cnt snand_page_per_blk')
  853. info_struct = _info_struct._make(unpack('<IIIIIIIIBBBBIIIIIHHBBBBIII', info))
  854. print("==== NAND ====")
  855. print("Page per block: " + str(info_struct.page_per_blk))
  856. print("Page size: " + str(info_struct.page_size))
  857. print("Block per flash: " + str(info_struct.blk_cnt))
  858. print("Bad block count: " + str(info_struct.bad_clk_cnt))
  859. print("Spare size: " + str(info_struct.oob_size))
  860. print("Is uer config: " + str(info_struct.usr_cfg0))
  861. print("==== SPI NOR ====")
  862. print("ID: " + str(info_struct.spi_id))
  863. print("Is uer config: " + str(info_struct.usr_cfg1))
  864. print("Quad read cmd: " + str(info_struct.quad_cmd))
  865. print("Read sts cmd: " + str(info_struct.read_sts_cmd))
  866. print("Write sts cmd: " + str(info_struct.write_sts_cmd))
  867. print("Sts value: " + str(info_struct.sts_val))
  868. print("Dummy byte: " + str(info_struct.dummy_byte))
  869. print("==== eMMC ====")
  870. print("Block: " + str(info_struct.blk))
  871. print("Reserved: " + str(info_struct.rsv))
  872. print("==== SPI NAND ====")
  873. print("Is uer config: " + str(info_struct.use_cfg2))
  874. print("ID: " + str(info_struct.snand_id))
  875. print("Page size: " + str(info_struct.snand_page_size))
  876. print("Spare size: " + str(info_struct.snand_oob))
  877. print("Quad read cmd: " + str(info_struct.snand_quad_cmd))
  878. print("Read sts cmd: " + str(info_struct.snand_read_sts_cmd))
  879. print("Write sts cmd: " + str(info_struct.snand_write_sts_cmd))
  880. print("Sts value: " + str(info_struct.snand_sts_val))
  881. print("Dummy byte: " + str(info_struct.snand_dummy_byte))
  882. print("Block per flash: " + str(info_struct.snand_blk_cnt))
  883. print("Page per block: " + str(info_struct.snand_page_per_blk))
  884. dev.set_align(info_struct.page_size * info_struct.page_per_blk,
  885. info_struct.snand_page_size * info_struct.snand_page_per_blk)
  886. return 0
  887. def do_attach(ini_file_name, option=OPT_NONE) -> None:
  888. global mp_mode
  889. init_location = "missing"
  890. if os.path.exists(ini_file_name): # default use the init file in current directory
  891. init_location = ini_file_name
  892. else:
  893. if platform.system() == 'Windows':
  894. if os.path.exists(WINDOWS_PATH + "ddrimg\\" + ini_file_name):
  895. init_location = WINDOWS_PATH + "ddrimg\\" + ini_file_name
  896. elif platform.system() == 'Linux':
  897. if os.path.exists(LINUX_PATH + "ddrimg/" + ini_file_name):
  898. init_location = LINUX_PATH + "ddrimg/" + ini_file_name
  899. if init_location == "missing":
  900. print(f"Cannot find {ini_file_name}")
  901. sys.exit(3)
  902. try:
  903. with open(init_location, "rb") as ini_file:
  904. ini_data = ini_file.read()
  905. except (IOError, OSError) as err:
  906. print(f"Open {ini_file_name} failed")
  907. sys.exit(err)
  908. xusb_location = "missing"
  909. if os.path.exists("xusb.bin"): # default use the xusb.bin in current directory
  910. xusb_location = "xusb.bin"
  911. else:
  912. if platform.system() == 'Windows':
  913. if os.path.exists(WINDOWS_PATH + "xusb.bin"):
  914. xusb_location = WINDOWS_PATH + "xusb.bin"
  915. elif platform.system() == 'Linux':
  916. if os.path.exists(LINUX_PATH + "xusb.bin"):
  917. xusb_location = LINUX_PATH + "xusb.bin"
  918. if xusb_location == "missing":
  919. print("Cannot find xusb.bin")
  920. sys.exit(3)
  921. try:
  922. with open(xusb_location, "rb") as xusb_file:
  923. xusb_data = xusb_file.read()
  924. except (IOError, OSError) as err:
  925. print("Open xusb.bin failed")
  926. sys.exit(err)
  927. # devices = XUsbComList(attach_all=mp_mode).get_dev()
  928. _XUsbComList = XUsbComList(attach_all=mp_mode)
  929. devices = _XUsbComList.get_dev()
  930. if len(devices) == 0:
  931. print("Device not found")
  932. sys.exit(2)
  933. with ThreadPoolExecutor(max_workers=8) as executor:
  934. futures = [executor.submit(__attach, dev, ini_data, xusb_data) for dev in devices]
  935. success = 0
  936. failed = 0
  937. for future in as_completed(futures, timeout=2):
  938. if future.result() == 0:
  939. success += 1
  940. else:
  941. failed += 1
  942. print(f"Successfully attached {success} device(s)")
  943. if failed > 0:
  944. print(f"Failed to attach {failed} device(s)")
  945. if success == 0:
  946. return
  947. time.sleep(1)
  948. # devices = XUsbComList(attach_all=mp_mode).get_dev()
  949. _XUsbComListNew = XUsbComList(attach_all=mp_mode)
  950. devices = _XUsbComListNew.get_dev()
  951. data = bytearray(76)
  952. # assign option file to set media info
  953. if option == OPT_SETINFO:
  954. try:
  955. with open("info.json", "r") as json_file:
  956. try:
  957. d = json.load(json_file)
  958. except json.decoder.JSONDecodeError as err:
  959. print(f"{json_file} parsing error")
  960. sys.exit(err)
  961. except (IOError, OSError) as err:
  962. print("Open info.json failed")
  963. sys.exit(err)
  964. # now generate info from info.json
  965. for key in d.keys():
  966. if key == 'spinand':
  967. data[48] = 1
  968. for sub_key in d['spinand'].keys():
  969. if sub_key == 'pagesize':
  970. data[56:58] = int(d['spinand']['pagesize'], 0).to_bytes(2, byteorder="little")
  971. elif sub_key == 'sparearea':
  972. data[58:60] = int(d['spinand']['sparearea'], 0).to_bytes(2, byteorder="little")
  973. elif sub_key == 'quadread':
  974. data[60:61] = int(d['spinand']['quadread'], 0).to_bytes(1, byteorder="little")
  975. elif sub_key == 'readsts':
  976. data[61:62] = int(d['spinand']['readsts'], 0).to_bytes(1, byteorder="little")
  977. elif sub_key == 'writests':
  978. data[62:63] = int(d['spinand']['writests'], 0).to_bytes(1, byteorder="little")
  979. elif sub_key == 'stsvalue':
  980. data[63:64] = int(d['spinand']['stsvalue'], 0).to_bytes(1, byteorder="little")
  981. elif sub_key == 'dummy':
  982. data[64:68] = int(d['spinand']['dummy'], 0).to_bytes(4, byteorder="little")
  983. elif sub_key == 'blkcnt':
  984. data[68:72] = int(d['spinand']['blkcnt'], 0).to_bytes(4, byteorder="little")
  985. elif sub_key == 'pageperblk':
  986. data[72:76] = int(d['spinand']['pageperblk'], 0).to_bytes(4, byteorder="little")
  987. elif key == 'spinor':
  988. data[28] = 1
  989. for sub_key in d['spinor'].keys():
  990. if sub_key == 'quadread':
  991. data[32:33] = int(d['spinor']['quadread'], 0).to_bytes(1, byteorder="little")
  992. elif sub_key == 'readsts':
  993. data[33:34] = int(d['spinor']['readsts'], 0).to_bytes(1, byteorder="little")
  994. elif sub_key == 'writests':
  995. data[34:35] = int(d['spinor']['writests'], 0).to_bytes(1, byteorder="little")
  996. elif sub_key == 'stsvalue':
  997. data[35:36] = int(d['spinor']['stsvalue'], 0).to_bytes(1, byteorder="little")
  998. elif sub_key == 'dummy':
  999. data[36:40] = int(d['spinor']['dummy'], 0).to_bytes(4, byteorder="little")
  1000. elif key == 'nand':
  1001. data[20] = 1
  1002. for sub_key in d['nand'].keys():
  1003. if sub_key == 'blkcnt':
  1004. data[8:12] = int(d['nand']['blkcnt'], 0).to_bytes(4, byteorder="little")
  1005. elif sub_key == 'pageperblk':
  1006. data[0:4] = int(d['nand']['pageperblk'], 0).to_bytes(4, byteorder="little")
  1007. if len(devices) == 0:
  1008. print("Device not found")
  1009. sys.exit(2)
  1010. with ThreadPoolExecutor(max_workers=8) as executor:
  1011. futures = [executor.submit(__get_info, dev, data) for dev in devices]
  1012. success = 0
  1013. failed = 0
  1014. for future in as_completed(futures, timeout=2):
  1015. if future.result() == 0:
  1016. success += 1
  1017. else:
  1018. failed += 1
  1019. print(f"Successfully get info from {success} device(s)")
  1020. def do_unpack(pack_file_name, nocrc32) -> None:
  1021. now = datetime.now()
  1022. pack_image = UnpackImage(pack_file_name, nocrc32)
  1023. image_cnt = pack_image.img_count()
  1024. try:
  1025. os.mkdir(now.strftime("%m%d-%H%M%S%f"))
  1026. except (IOError, OSError) as err:
  1027. sys.exit(err)
  1028. for i in range(image_cnt):
  1029. img_length, _, _ = pack_image.img_attr(i)
  1030. try:
  1031. with open(now.strftime("%m%d-%H%M%S%f") + "/img" + str(i) + ".bin", "wb") as img_file:
  1032. img_file.write(pack_image.img_content(i, 0, img_length))
  1033. except (IOError, OSError) as err:
  1034. print("Create output image file failed")
  1035. sys.exit(err)
  1036. try:
  1037. os.unlink("unpack")
  1038. except (IOError, OSError):
  1039. pass
  1040. try:
  1041. os.symlink(now.strftime("%m%d-%H%M%S%f"), "unpack")
  1042. except (IOError, OSError):
  1043. print("Create symbolic folder unpack failed")
  1044. print("Unpack images to directory {} complete".format(now.strftime("%m%d-%H%M%S%f")))
  1045. def do_stuff(cfg_file) -> None:
  1046. now = datetime.now()
  1047. try:
  1048. with open(cfg_file, "r") as json_file:
  1049. try:
  1050. d = json.load(json_file)
  1051. except json.decoder.JSONDecodeError as err:
  1052. print(f"{cfg_file} parsing error")
  1053. sys.exit(err)
  1054. except (IOError, OSError) as err:
  1055. print(f"Open {cfg_file} failed")
  1056. sys.exit(err)
  1057. try:
  1058. os.mkdir(now.strftime("%m%d-%H%M%S%f"))
  1059. pack_file = open(now.strftime("%m%d-%H%M%S%f") + "/pack.bin", "wb")
  1060. except (IOError, OSError) as err:
  1061. sys.exit(err)
  1062. offset = 0
  1063. out = bytearray()
  1064. # Start stuffing image
  1065. for img in d["image"]:
  1066. try:
  1067. with open(img["file"], "rb") as img_file:
  1068. data = img_file.read()
  1069. except (IOError, OSError) as err:
  1070. print(f"Open {img_file} failed")
  1071. shutil.rmtree(now.strftime("%m%d-%H%M%S%f"))
  1072. sys.exit(err)
  1073. if int(img["offset"], 0) < offset:
  1074. print(f"Please place the files in {cfg_file} based on the ascending offset")
  1075. sys.exit(4)
  1076. elif int(img["offset"], 0) > offset:
  1077. out += b'\xFF' * (int(img["offset"], 0) - offset)
  1078. offset = int(img["offset"], 0)
  1079. out += data
  1080. offset += len(data)
  1081. pack_file.write(out)
  1082. pack_file.close()
  1083. try:
  1084. os.unlink("pack")
  1085. except (IOError, OSError):
  1086. pass
  1087. try:
  1088. os.symlink(now.strftime("%m%d-%H%M%S%f"), "pack")
  1089. except (IOError, OSError):
  1090. print("Create symbolic folder pack failed")
  1091. print("Generate pack file in directory {} complete".format(now.strftime("%m%d-%H%M%S%f")))
  1092. def do_pack(cfg_file) -> None:
  1093. now = datetime.now()
  1094. try:
  1095. with open(cfg_file, "r") as json_file:
  1096. try:
  1097. d = json.load(json_file)
  1098. except json.decoder.JSONDecodeError as err:
  1099. print(f"{cfg_file} parsing error")
  1100. sys.exit(err)
  1101. except (IOError, OSError) as err:
  1102. print(f"Open {cfg_file} failed")
  1103. sys.exit(err)
  1104. try:
  1105. os.mkdir(now.strftime("%m%d-%H%M%S%f"))
  1106. pack_file = open(now.strftime("%m%d-%H%M%S%f") + "/pack.bin", "wb")
  1107. except (IOError, OSError) as err:
  1108. sys.exit(err)
  1109. out = bytearray(b'\x20\x54\x56\x4e' + b'\xFF' * 12) # NVT + CRC32 + image count + 4 reserved bytes
  1110. # Start packing image
  1111. img_cnt = 0
  1112. for img in d["image"]:
  1113. try:
  1114. with open(img["file"], "rb") as img_file:
  1115. data = img_file.read()
  1116. except (IOError, OSError) as err:
  1117. print(f"Open {img_file} failed")
  1118. shutil.rmtree(now.strftime("%m%d-%H%M%S%f"))
  1119. sys.exit(err)
  1120. img_cnt = img_cnt + 1
  1121. img_len = len(data)
  1122. out += img_len.to_bytes(8, byteorder="little")
  1123. try:
  1124. out += int(img["offset"], 0).to_bytes(8, byteorder="little")
  1125. except ValueError as err:
  1126. shutil.rmtree(now.strftime("%m%d-%H%M%S%f"))
  1127. sys.exit(err)
  1128. out += img["type"].to_bytes(4, byteorder="little")
  1129. out += b'\xFF' * 4
  1130. out += data
  1131. # Always put image start @ 16 byte boundary
  1132. pad = 16 - (img_len + 8) & 0xF
  1133. if pad != 16:
  1134. out += b'\xFF' * pad
  1135. # Fill image count
  1136. out[8:12] = img_cnt.to_bytes(4, byteorder="little")
  1137. # Fill CRC field
  1138. crc32_func = crcmod.predefined.mkCrcFun('crc-32')
  1139. checksum = crc32_func(out[8:])
  1140. out[4:8] = checksum.to_bytes(4, byteorder="little")
  1141. pack_file.write(out)
  1142. pack_file.close()
  1143. try:
  1144. os.unlink("pack")
  1145. except (IOError, OSError):
  1146. pass
  1147. try:
  1148. os.symlink(now.strftime("%m%d-%H%M%S%f"), "pack")
  1149. except (IOError, OSError):
  1150. print("Create symbolic folder pack failed")
  1151. print("Generate pack file in directory {} complete".format(now.strftime("%m%d-%H%M%S%f")))
  1152. def do_showhdr(cfg_file) -> None:
  1153. try:
  1154. header_file = open(cfg_file, "br")
  1155. except (IOError, OSError) as err:
  1156. print(f"Open {cfg_file} failed")
  1157. sys.exit(err)
  1158. if unpack('<I', header_file.read(4))[0] != 0x4E565420:
  1159. print("Header checker error, not a valid header image")
  1160. header_file.close()
  1161. return
  1162. checksum0 = unpack('<I', header_file.read(4))[0]
  1163. buf = header_file.read()
  1164. crc32_func = crcmod.predefined.mkCrcFun('crc-32')
  1165. checksum1 = crc32_func(buf)
  1166. if checksum1 != checksum0:
  1167. print("Checksum is incorrect")
  1168. print(f"Expect {checksum1}, get {checksum0}")
  1169. header_file.close()
  1170. return
  1171. header_file.seek(8)
  1172. print("Length: " + str(unpack('<I', header_file.read(4))[0]))
  1173. print("Version: " + hex(unpack('<I', header_file.read(4))[0]))
  1174. print("==== SPI INFO ====")
  1175. print("Page size: " + str(unpack('<H', header_file.read(2))[0]))
  1176. print("Spare area size: " + str(unpack('<H', header_file.read(2))[0]))
  1177. print("Page per block: " + str(unpack('<H', header_file.read(2))[0]))
  1178. print("Quad read cmd: " + hex(unpack('<B', header_file.read(1))[0]))
  1179. print("Read status cmd: " + hex(unpack('<B', header_file.read(1))[0]))
  1180. print("Write status cmd: " + hex(unpack('<B', header_file.read(1))[0]))
  1181. print("Status Value: " + hex(unpack('<B', header_file.read(1))[0]))
  1182. print("Dummy byte 1: " + str(unpack('<B', header_file.read(1))[0]))
  1183. print("Dummy byte 2: " + str(unpack('<B', header_file.read(1))[0]))
  1184. print("Suspend interval: " + str(unpack('<B', header_file.read(1))[0]))
  1185. header_file.read(3) # Skip three dummy bytes
  1186. print("==== SPI INFO ====")
  1187. print("Entry point: " + hex(unpack('<I', header_file.read(4))[0]))
  1188. count = unpack('<I', header_file.read(4))[0]
  1189. print(f"Image count: {count}")
  1190. for i in range(0, count):
  1191. print(f"==== Image {i} ====")
  1192. print("Offset: " + hex(unpack('<I', header_file.read(4))[0]))
  1193. print("Load addr: " + hex(unpack('<I', header_file.read(4))[0]))
  1194. print("Size: " + hex(unpack('<I', header_file.read(4))[0]))
  1195. print("Type: " + str(unpack('<I', header_file.read(4))[0]))
  1196. print("R: ", end='')
  1197. for _ in range(0, 32):
  1198. print(format(unpack('<B', header_file.read(1))[0], 'x'), end='')
  1199. print("")
  1200. print("S: ", end='')
  1201. for _ in range(0, 32):
  1202. print(format(unpack('<B', header_file.read(1))[0], 'x'), end='')
  1203. print("")
  1204. header_file.close()
  1205. def do_convert(cfg_file, option=OPT_NONE) -> None:
  1206. now = datetime.now()
  1207. try:
  1208. with open(cfg_file, "r") as json_file:
  1209. try:
  1210. d = json.load(json_file)
  1211. except json.decoder.JSONDecodeError as err:
  1212. print(f"{cfg_file} parsing error")
  1213. sys.exit(err)
  1214. except (IOError, OSError) as err:
  1215. print(f"Open {cfg_file} failed")
  1216. sys.exit(err)
  1217. try:
  1218. os.mkdir(now.strftime("%m%d-%H%M%S%f"))
  1219. except (IOError, OSError) as err:
  1220. print("Create output directory failed")
  1221. sys.exit(err)
  1222. if "header" in d:
  1223. out = bytearray(b'\x20\x54\x56\x4e' + b'\x00' * 8) # NVT + CRC + LEN
  1224. try:
  1225. out += int(d["header"]["version"], 0).to_bytes(4, byteorder="little")
  1226. # Fill SPI flash info
  1227. out += int(d["header"]["spiinfo"]["pagesize"], 0).to_bytes(2, byteorder="little")
  1228. out += int(d["header"]["spiinfo"]["sparearea"], 0).to_bytes(2, byteorder="little")
  1229. out += int(d["header"]["spiinfo"]["pageperblk"], 0).to_bytes(2, byteorder="little")
  1230. out += int(d["header"]["spiinfo"]["quadread"], 0).to_bytes(1, byteorder="little")
  1231. out += int(d["header"]["spiinfo"]["readsts"], 0).to_bytes(1, byteorder="little")
  1232. out += int(d["header"]["spiinfo"]["writests"], 0).to_bytes(1, byteorder="little")
  1233. out += int(d["header"]["spiinfo"]["stsvalue"], 0).to_bytes(1, byteorder="little")
  1234. out += int(d["header"]["spiinfo"]["dummy1"], 0).to_bytes(1, byteorder="little")
  1235. out += int(d["header"]["spiinfo"]["dummy2"], 0).to_bytes(1, byteorder="little")
  1236. out += int(d["header"]["spiinfo"]["suspintvl"], 0).to_bytes(1, byteorder="little")
  1237. out += b'\xFF' * 3 # 3 reserved bytes
  1238. out += int(d["header"]["entrypoint"], 0).to_bytes(4, byteorder="little")
  1239. except ValueError as err:
  1240. shutil.rmtree(now.strftime("%m%d-%H%M%S%f"))
  1241. sys.exit(err)
  1242. out += b'\xFF' * 4 # Reserve 4 bytes for image count
  1243. # Generate key file iff secure boot is enabled
  1244. if d["header"]["secureboot"] == 'yes':
  1245. try:
  1246. key_file = open(now.strftime("%m%d-%H%M%S%f") + "/header_key.txt", "w+")
  1247. except (IOError, OSError) as err:
  1248. print("Create key file failed")
  1249. shutil.rmtree(now.strftime("%m%d-%H%M%S%f"))
  1250. sys.exit(err)
  1251. if "aeskey" in d["header"]:
  1252. try:
  1253. aeskey = bytes.fromhex(d["header"]["aeskey"])
  1254. except ValueError as err:
  1255. sys.exit(err)
  1256. else:
  1257. aeskey = ''.join(['%x' % random.randrange(16) for _ in range(0, 64)])
  1258. aeskey = binascii.unhexlify(bytes(aeskey, 'utf-8'))
  1259. key_file.write("AES key:\n" + str.upper(aeskey.hex()))
  1260. if "ecdsakey" in d["header"]:
  1261. try:
  1262. sk = ecdsa.SigningKey.from_string(bytes.fromhex(d["header"]["ecdsakey"]),
  1263. curve=ecdsa.NIST256p,
  1264. hashfunc=hashlib.sha256)
  1265. except ValueError as err:
  1266. sys.exit(err)
  1267. else:
  1268. sk = ecdsa.SigningKey.generate(curve=ecdsa.NIST256p, hashfunc=hashlib.sha256)
  1269. key_file.write("\nECDSA private key:\n" + str.upper(sk.to_string().hex()))
  1270. vk = sk.verifying_key
  1271. key_file.write("\nECDSA public key:\n" + format(vk.pubkey.point.x(), 'X') +
  1272. "\n" + format(vk.pubkey.point.y(), 'X') + "\n")
  1273. key_file.close()
  1274. img_cnt = len(d["header"]["image"])
  1275. if img_cnt > MAX_HEADER_IMG:
  1276. print("Can process 4 images in header max")
  1277. shutil.rmtree(now.strftime("%m%d-%H%M%S%f"))
  1278. sys.exit(2)
  1279. # Fill image information
  1280. for i in range(img_cnt):
  1281. img = d["header"]["image"][i]
  1282. try:
  1283. with open(img["file"], "rb") as img_file:
  1284. data = img_file.read()
  1285. except (IOError, OSError) as err:
  1286. print("Open image file failed")
  1287. shutil.rmtree(now.strftime("%m%d-%H%M%S%f"))
  1288. sys.exit(err)
  1289. try:
  1290. out += int(img["offset"], 0).to_bytes(4, byteorder="little")
  1291. out += int(img["loadaddr"], 0).to_bytes(4, byteorder="little")
  1292. out += os.path.getsize(img["file"]).to_bytes(4, byteorder="little")
  1293. out += int(img["type"]).to_bytes(4, byteorder="little")
  1294. except ValueError as err:
  1295. shutil.rmtree(now.strftime("%m%d-%H%M%S%f"))
  1296. sys.exit(err)
  1297. if d["header"]["secureboot"] == 'yes':
  1298. # Use CFB and each image is process independently, so call new() for every image
  1299. aes_enc = AES.new(aeskey, AES.MODE_CFB, b'\x00' * 16, segment_size=128)
  1300. data_out = aes_enc.encrypt(data)
  1301. # R & S
  1302. out += sk.sign(data_out)
  1303. # Write encrypt image
  1304. try:
  1305. with open(now.strftime("%m%d-%H%M%S%f") + '/enc_' +
  1306. os.path.basename(img["file"]), "wb") as enc_file:
  1307. enc_file.write(data_out)
  1308. except (IOError, OSError) as err:
  1309. print("Create encrypt file failed")
  1310. shutil.rmtree(now.strftime("%m%d-%H%M%S%f"))
  1311. sys.exit(err)
  1312. else:
  1313. out += b'\xFF' * 64 # Just pack 0xFF if secure boot is disabled
  1314. # Fill header length
  1315. out[8:12] = int(len(out) - 8).to_bytes(4, byteorder="little")
  1316. # Fill image count
  1317. out[36:40] = img_cnt.to_bytes(4, byteorder="little")
  1318. # Fill header checksum
  1319. crc32_func = crcmod.predefined.mkCrcFun('crc-32')
  1320. out[4:8] = crc32_func(out[8:]).to_bytes(4, byteorder="little")
  1321. try:
  1322. with open(now.strftime("%m%d-%H%M%S%f") + "/header.bin", "wb") as header_file:
  1323. header_file.write(out)
  1324. except (IOError, OSError) as err:
  1325. print("Create header file failed")
  1326. sys.exit(err)
  1327. if "env" in d:
  1328. try:
  1329. with open(now.strftime("%m%d-%H%M%S%f") + "/uboot-env.bin", "wb") as out_file:
  1330. out_file.write(conv_env(d["env"]["file"], int(d["env"]["blksize"], 0)))
  1331. except (IOError, OSError, ValueError) as err:
  1332. print("Create header file failed")
  1333. sys.exit(err)
  1334. # Misc images
  1335. if "data" in d:
  1336. try:
  1337. key_file = open(now.strftime("%m%d-%H%M%S%f") + "/data_key.txt", "w+")
  1338. except (IOError, OSError) as err:
  1339. print("Create key file failed")
  1340. shutil.rmtree(now.strftime("%m%d-%H%M%S%f"))
  1341. sys.exit(err)
  1342. if "aeskey" in d["data"]:
  1343. try:
  1344. aeskey = bytes.fromhex(d["data"]["aeskey"])
  1345. except ValueError as err:
  1346. sys.exit(err)
  1347. else:
  1348. aeskey = ''.join(['%x' % random.randrange(16) for _ in range(0, 64)])
  1349. aeskey = binascii.unhexlify(bytes(aeskey, 'utf-8'))
  1350. key_file.write("AES key:\n" + str.upper(aeskey.hex()))
  1351. if "ecdsakey" in d["data"]:
  1352. try:
  1353. sk = ecdsa.SigningKey.from_string(bytes.fromhex(d["data"]["ecdsakey"]),
  1354. curve=ecdsa.NIST256p,
  1355. hashfunc=hashlib.sha256)
  1356. except ValueError as err:
  1357. sys.exit(err)
  1358. else:
  1359. sk = ecdsa.SigningKey.generate(curve=ecdsa.NIST256p, hashfunc=hashlib.sha256)
  1360. key_file.write("\nECDSA private key:\n" + str.upper(sk.to_string().hex()))
  1361. vk = sk.verifying_key
  1362. key_file.write("\nECDSA public key:\n" + format(vk.pubkey.point.x(), 'X') +
  1363. "\n" + format(vk.pubkey.point.y(), 'X') + "\n")
  1364. key_file.close()
  1365. for img in d["data"]["image"]:
  1366. try:
  1367. with open(img["file"], "rb") as img_file:
  1368. data = img_file.read()
  1369. except (IOError, OSError) as err:
  1370. print(f"Open {img_file} failed")
  1371. shutil.rmtree(now.strftime("%m%d-%H%M%S%f"))
  1372. sys.exit(err)
  1373. aes_enc = AES.new(aeskey, AES.MODE_CFB, b'\x00' * 16, segment_size=128)
  1374. data_out = aes_enc.encrypt(data)
  1375. signature = sk.sign(data_out)
  1376. try:
  1377. if option is OPT_CONCAT:
  1378. # Append the R & S at the end of signed image instead of writing to a separate file.
  1379. # The signed image could be used in deployed mode
  1380. data_out += signature
  1381. else:
  1382. with open(now.strftime("%m%d-%H%M%S%f") + '/sig_' + img["file"], "wb") as sig_file:
  1383. sig_file.write(signature) # R & S
  1384. with open(now.strftime("%m%d-%H%M%S%f") + '/enc_' + img["file"], "wb") as enc_file:
  1385. enc_file.write(data_out)
  1386. except (IOError, OSError) as err:
  1387. print("Create encrypt/signature file failed")
  1388. shutil.rmtree(now.strftime("%m%d-%H%M%S%f"))
  1389. sys.exit(err)
  1390. try:
  1391. os.unlink("conv")
  1392. except (IOError, OSError):
  1393. pass
  1394. try:
  1395. os.symlink(now.strftime("%m%d-%H%M%S%f"), "conv")
  1396. except (IOError, OSError):
  1397. print("Create symbolic folder conv failed")
  1398. print("Generate output image(s) in directory {} complete".format(now.strftime("%m%d-%H%M%S%f")))
  1399. def __msc(dev, media, reserve, option) -> int:
  1400. cmd = reserve.to_bytes(8, byteorder='little')
  1401. cmd += b'\x00' * 8
  1402. cmd += ACT_MSC.to_bytes(4, byteorder='little')
  1403. cmd += option.to_bytes(4, byteorder='little')
  1404. dev.set_media(media)
  1405. dev.write(cmd)
  1406. ack = dev.read(4)
  1407. if int.from_bytes(ack, byteorder="little") != ACK:
  1408. print("Receive ACK error")
  1409. return -1
  1410. return 0
  1411. def do_msc(media, reserve, option=OPT_NONE) -> None:
  1412. global mp_mode
  1413. # devices = XUsbComList(attach_all=mp_mode).get_dev()
  1414. _XUsbComList = XUsbComList(attach_all=mp_mode)
  1415. devices = _XUsbComList.get_dev()
  1416. if len(devices) == 0:
  1417. print("Device not found")
  1418. sys.exit(2)
  1419. with ThreadPoolExecutor(max_workers=8) as executor:
  1420. futures = [executor.submit(__msc, dev, media, reserve, option) for dev in devices]
  1421. success = 0
  1422. failed = 0
  1423. for future in as_completed(futures):
  1424. if future.result() == 0:
  1425. success += 1
  1426. else:
  1427. failed += 1
  1428. print("Successfully {} {} MSC device(s)".format("set" if option == "OPT_NONE" else "eject", success))
  1429. if failed > 0:
  1430. print("Failed to {} {} MSC device(s)".format("set" if option == "OPT_NONE" else "eject", failed))
  1431. def get_media(media) -> int:
  1432. media = str.upper(media)
  1433. return {
  1434. 'DDR': DEV_DDR_SRAM,
  1435. 'SRAM': DEV_DDR_SRAM,
  1436. 'SD': DEV_SD_EMMC,
  1437. 'EMMC': DEV_SD_EMMC,
  1438. 'NAND': DEV_NAND,
  1439. 'SPINAND': DEV_SPINAND,
  1440. 'SPINOR': DEV_SPINOR,
  1441. 'OTP': DEV_OTP,
  1442. 'USBH': DEV_USBH
  1443. }.get(media, DEV_UNKNOWN)
  1444. def get_option(option) -> int:
  1445. option = str.upper(option)
  1446. return {
  1447. 'SCRUB': OPT_SCRUB,
  1448. 'WITHBAD': OPT_WITHBAD,
  1449. 'VERIFY': OPT_VERIFY,
  1450. 'EXECUTE': OPT_EXECUTE,
  1451. 'UNPACK': OPT_UNPACK,
  1452. 'RAW': OPT_RAW,
  1453. 'EJECT': OPT_EJECT,
  1454. 'STUFF': OPT_STUFF,
  1455. 'SETINFO': OPT_SETINFO,
  1456. 'CONCAT': OPT_CONCAT,
  1457. 'SHOWHDR': OPT_SHOWHDR,
  1458. 'NOCRC': OPT_NOCRC
  1459. }.get(option, OPT_UNKNOWN)
  1460. def get_type(img_type) -> int:
  1461. img_type = str.upper(img_type)
  1462. return {
  1463. 'TFA': IMG_TFA,
  1464. 'UBOOT': IMG_UBOOT,
  1465. 'LINUX': IMG_LINUX,
  1466. 'DDR': IMG_DDR,
  1467. 'TEE': IMG_TEE
  1468. }.get(img_type, IMG_DATA)
  1469. def get_otpblock(num) -> int:
  1470. num = str.upper(num)
  1471. return {
  1472. 'OTP1': OPT_OTPBLK1,
  1473. 'OTP2': OPT_OTPBLK2,
  1474. 'OTP3': OPT_OTPBLK3,
  1475. 'OTP4': OPT_OTPBLK4,
  1476. 'OTP5': OPT_OTPBLK5,
  1477. 'OTP6': OPT_OTPBLK6,
  1478. 'OTP7': OPT_OTPBLK7,
  1479. 'KEY0': OPT_OTPKEY0+OPT_OTPKEY,
  1480. 'KEY1': OPT_OTPKEY1+OPT_OTPKEY,
  1481. 'KEY2': OPT_OTPKEY2+OPT_OTPKEY,
  1482. 'KEY3': OPT_OTPKEY3+OPT_OTPKEY,
  1483. 'KEY4': OPT_OTPKEY4+OPT_OTPKEY,
  1484. 'KEY5': OPT_OTPKEY5+OPT_OTPKEY
  1485. }.get(num, OPT_UNKNOWN)
  1486. def main():
  1487. parser = argparse.ArgumentParser()
  1488. parser.add_argument("CONFIG", nargs='?', help="Config file", type=str, default='')
  1489. parser.add_argument("-a", "--attach", action='store_true', help="Attach to MA35D1")
  1490. parser.add_argument("-o", "--option", nargs='+', help="Option flag")
  1491. parser.add_argument("-t", "--type", nargs='+', help="Type flag")
  1492. group = parser.add_mutually_exclusive_group()
  1493. group.add_argument("-c", "--convert", action='store_true', help="Convert images")
  1494. group.add_argument("-p", "--pack", action='store_true', help="Generate pack file")
  1495. group.add_argument("-v", "--version", action='store_true', help="Show version number")
  1496. group.add_argument("-r", "--read", nargs='+', help="Read flash")
  1497. group.add_argument("-w", "--write", nargs='+', help="Write flash")
  1498. group.add_argument("-e", "--erase", nargs='+', help="Erase flash")
  1499. group.add_argument("-s", "--storage", nargs='+', help="Export eMMC/SD as Mass Storage Class")
  1500. if len(sys.argv) == 1:
  1501. parser.print_help()
  1502. sys.exit(0)
  1503. args = parser.parse_args()
  1504. if args.option:
  1505. option = get_option(args.option[0])
  1506. else:
  1507. option = OPT_NONE
  1508. if option is OPT_UNKNOWN:
  1509. print("Unknown option: " + args.option[0])
  1510. sys.exit(0)
  1511. # if args.type:
  1512. # img_type = get_type(args.type[0])
  1513. # else:
  1514. # img_type = IMG_DATA
  1515. cfg_file = args.CONFIG
  1516. if args.attach:
  1517. if not cfg_file:
  1518. print("Please assign a DDR ini file")
  1519. sys.exit(0)
  1520. do_attach(cfg_file, option)
  1521. if args.convert:
  1522. if cfg_file == '':
  1523. print("No config file assigned")
  1524. sys.exit(0)
  1525. else:
  1526. if option == OPT_SHOWHDR:
  1527. do_showhdr(cfg_file)
  1528. else:
  1529. do_convert(cfg_file, option)
  1530. elif args.pack:
  1531. if cfg_file == '':
  1532. print("No config file assigned")
  1533. sys.exit(0)
  1534. else:
  1535. if option == OPT_UNPACK:
  1536. do_unpack(cfg_file, 0)
  1537. elif option == OPT_NOCRC:
  1538. do_unpack(cfg_file, 1)
  1539. elif option == OPT_STUFF:
  1540. do_stuff(cfg_file)
  1541. else:
  1542. do_pack(cfg_file)
  1543. elif args.read:
  1544. # -r spinor all out.bin
  1545. # -r nand 0x1000 0x100 out.bin
  1546. # -r otp all out.bin (block1 ~ block7)
  1547. # -r otp blockno 0x4 out.bin
  1548. arg_count = len(args.read)
  1549. if arg_count < 3:
  1550. print("At lease take 3 arguments")
  1551. sys.exit(0)
  1552. media = get_media(args.read[0])
  1553. try:
  1554. if media == DEV_UNKNOWN:
  1555. raise ValueError(f"Cannot support read {str.upper(args.read[0])}")
  1556. if arg_count == 3 and str.upper(args.read[1]) != 'ALL':
  1557. raise ValueError("Unknown arguments")
  1558. except ValueError as err:
  1559. sys.exit(err)
  1560. if str.upper(args.read[1]) == 'ALL':
  1561. if media == DEV_OTP:
  1562. option |= 0x3fff00
  1563. do_otp_read(media, 0, args.read[2], 352, option)
  1564. else:
  1565. do_img_read(media, 0, args.read[2], 0, option)
  1566. else:
  1567. try:
  1568. if media == DEV_OTP:
  1569. option = get_otpblock(args.read[1]) | option
  1570. else:
  1571. start = int(args.read[1], 0)
  1572. length = int(args.read[2], 0)
  1573. except ValueError as err:
  1574. print("Wrong start/length value")
  1575. sys.exit(err)
  1576. if media == DEV_OTP:
  1577. do_otp_read(media, 0, args.read[3], length, option)
  1578. else:
  1579. do_img_read(media, start, args.read[3], length, option)
  1580. elif args.write:
  1581. # -w spinor 0x1000 image.bin
  1582. # -w otp otp.json
  1583. # -w nand pack.img
  1584. arg_count = len(args.write)
  1585. if arg_count < 2:
  1586. print("At lease take 2 arguments")
  1587. sys.exit(0)
  1588. media = get_media(args.write[0])
  1589. try:
  1590. if media == DEV_UNKNOWN:
  1591. raise ValueError(f"Unknown storage media {str.upper(args.write[0])}")
  1592. if option == OPT_VERIFY and media == DEV_OTP:
  1593. raise ValueError(f"Do not support verify option on {str.upper(args.write[0])}")
  1594. if option == OPT_EXECUTE and media != DEV_DDR_SRAM:
  1595. raise ValueError(f"Do not support execution on {str.upper(args.write[0])}")
  1596. if option == OPT_RAW and media != DEV_NAND:
  1597. raise ValueError(f"Do not support raw write on {str.upper(args.write[0])}")
  1598. except ValueError as err:
  1599. sys.exit(err)
  1600. if arg_count == 2:
  1601. if media == DEV_OTP:
  1602. do_otp_program(args.write[1])
  1603. else:
  1604. do_pack_program(media, args.write[1], option)
  1605. else:
  1606. try:
  1607. start = int(args.write[1], 0)
  1608. except ValueError as err:
  1609. print("Wrong start value")
  1610. sys.exit(err)
  1611. do_img_program(media, start, args.write[2], option)
  1612. elif args.erase:
  1613. # -e spinor all
  1614. # -e nand 0x100000 0x10000 -o withbad
  1615. # -e otp blockno
  1616. arg_count = len(args.erase)
  1617. if arg_count < 2:
  1618. print("At lease take 2 arguments")
  1619. sys.exit(0)
  1620. media = get_media(args.erase[0])
  1621. try:
  1622. if media in [DEV_DDR_SRAM, DEV_SD_EMMC, DEV_UNKNOWN]:
  1623. raise ValueError(f"{str.upper(args.erase[0])} does not support erase")
  1624. if arg_count == 2 and str.upper(args.erase[1]) != 'ALL' and media != DEV_OTP:
  1625. raise ValueError("Unknown arguments")
  1626. if str.upper(args.erase[1]) == 'ALL' and media == DEV_OTP:
  1627. raise ValueError("Wrong arguments")
  1628. except ValueError as err:
  1629. sys.exit(err)
  1630. if media == DEV_OTP:
  1631. option = get_otpblock(args.erase[1])
  1632. try:
  1633. # only can erase block 1, 3, 4
  1634. if option == OPT_OTPBLK2:
  1635. raise ValueError(f"Error block 2, only erase block 1, 3, 4")
  1636. if option == OPT_OTPBLK5:
  1637. raise ValueError(f"Error block 5, only erase block 1, 3, 4")
  1638. if option == OPT_OTPBLK6:
  1639. raise ValueError(f"Error block 6, only erase block 1, 3, 4")
  1640. if option == OPT_OTPBLK7:
  1641. raise ValueError(f"Error block 7, only erase block 1, 3, 4")
  1642. if option == OPT_UNKNOWN:
  1643. raise ValueError(f"Error key number")
  1644. except ValueError as err:
  1645. sys.exit(err)
  1646. do_otp_erase(option)
  1647. else:
  1648. if str.upper(args.erase[1]) == 'ALL':
  1649. do_img_erase(media, 0, 0, option)
  1650. else:
  1651. try:
  1652. start = int(args.erase[1], 0)
  1653. length = int(args.erase[2], 0)
  1654. except ValueError as err:
  1655. print("Wrong start/length value")
  1656. sys.exit(err)
  1657. do_img_erase(media, start, length, option)
  1658. elif args.storage:
  1659. # -s emmc 0x800000
  1660. # -s emmc -o remove
  1661. arg_count = len(args.erase)
  1662. if arg_count != 2 and option != OPT_EJECT:
  1663. print("Takes 2 arguments. Storage device and reserved size")
  1664. sys.exit(0)
  1665. media = get_media(args.storage[0])
  1666. try:
  1667. if media not in [DEV_SD_EMMC]:
  1668. raise ValueError("Only support eMMC/SD")
  1669. if option != OPT_NONE and option != OPT_EJECT:
  1670. raise ValueError("Unsupported option")
  1671. except ValueError as err:
  1672. sys.exit(err)
  1673. if option == OPT_EJECT:
  1674. do_msc(media, 0, OPT_EJECT)
  1675. else:
  1676. try:
  1677. reserve = int(args.storage[1], 0)
  1678. except ValueError as err:
  1679. print("Wrong reserve size")
  1680. sys.exit(err)
  1681. do_msc(media, reserve)
  1682. elif args.version:
  1683. print('NuWriter ' + __version__)
  1684. print(__copyright__)
  1685. # Here goes the main function
  1686. if __name__ == "__main__":
  1687. #os.system("start cmd.exe") #Call do it. Will open cmd window in each process
  1688. main()