modemgen.py 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042
  1. #!/usr/bin/env python3
  2. # Copyright (C) 2018 RDA Technologies Limited and/or its affiliates("RDA").
  3. # All rights reserved.
  4. #
  5. # This software is supplied "AS IS" without any warranties.
  6. # RDA assumes no responsibility or liability for the use of the software,
  7. # conveys no license or title under any patent, copyright, or mask work
  8. # right to the product. RDA reserves the right to make changes in the
  9. # software without notification. RDA also make no representation or
  10. # warranty that such application will be suitable for the specified use
  11. # without further testing or modification.
  12. import argparse
  13. import sys
  14. import struct
  15. import shutil
  16. import os
  17. import glob
  18. import subprocess
  19. import json
  20. DESCRIPTION = """
  21. Create modem image use fbdevgen
  22. """
  23. FLAG_LZMA = 0x100
  24. FLAG_LZMA2 = 0x200
  25. FLAG_LZMA3 = 0x400
  26. FLAG_LZMA_MASK = 0x700
  27. CPIO_FILE_FORMAT = '=2s12H{}s{}s'
  28. CPIO_OLDLE_MAGIC = b'\xc7\x71'
  29. # Create directory if not exists
  30. def ensure_dir(dname):
  31. if dname and not os.path.exists(dname):
  32. os.makedirs(dname)
  33. # Collect nvm files from prj. It is required that nvm are located in
  34. # the same directory of prj
  35. def nvm_from_prj(fname):
  36. nvms = []
  37. pdir = os.path.dirname(fname)
  38. with open(fname, 'r') as fh:
  39. for line in fh.readlines():
  40. line = line.strip()
  41. if line.startswith('#') or not line:
  42. continue
  43. if line.startswith('MODULE'):
  44. fields = line.split()
  45. if len(fields) == 3:
  46. nvms.append(os.path.join(pdir, fields[2]))
  47. return nvms
  48. # Collect modules from prj.
  49. def modules_from_prj(fname):
  50. modules = []
  51. with open(fname, 'r') as fh:
  52. for line in fh.readlines():
  53. line = line.strip()
  54. if line.startswith('#') or not line:
  55. continue
  56. if line.startswith('MODULE'):
  57. fields = line.split()
  58. if len(fields) == 3:
  59. modules.append(os.path.basename(fields[2]))
  60. return modules
  61. # Get generated bin file name from prj
  62. def nvbin_from_prj(prj):
  63. with open(prj, 'r') as fh:
  64. for line in fh.readlines():
  65. line = line.strip()
  66. if line.startswith('#') or not line:
  67. continue
  68. if line.startswith('TARGET'):
  69. fields = line.split()
  70. if len(fields) != 3:
  71. return None
  72. return os.path.join(os.path.dirname(prj), fields[2])
  73. return None
  74. # Merge prj. The header is fixed, and MODULE are merged.
  75. def nvm_prj_merge(dst, srcs):
  76. dstdir = os.path.dirname(dst)
  77. nvms = []
  78. for src in srcs:
  79. nvms.extend(nvm_from_prj(src))
  80. nvms = copy_files(dstdir, nvms)
  81. with open(dst, 'w') as fh:
  82. fh.write('# SpreadTrum NVEditor Project File, Format Version 1.0\n')
  83. fh.write('PROJECT = NVitem\n')
  84. fh.write('ALIGNMENT = 4\n')
  85. fh.write('TARGET = nvitem.bin\n')
  86. fh.write('DESCRIPTION =\n')
  87. for nvm in nvms:
  88. fh.write('MODULE = {}\n'.format(os.path.basename(nvm)))
  89. return
  90. # Hack memlist. The flags will be replace by modem configuration
  91. def hack_mem_list(outfname, infname, cfg):
  92. with open(infname, 'rb') as fh:
  93. indata = fh.read()
  94. pos = 0
  95. insize = len(indata)
  96. outdata = bytearray()
  97. while pos < insize:
  98. name, address, size, flags = struct.unpack(
  99. '20sIII', indata[pos:pos+32])
  100. pos += 32
  101. flags &= ~FLAG_LZMA_MASK
  102. rname = name.rstrip(b'\x00').decode('utf-8')
  103. for mbin in cfg:
  104. if mbin['file'] == rname:
  105. if 'lzma' in mbin:
  106. flags |= FLAG_LZMA
  107. elif 'lzma2' in mbin:
  108. flags |= FLAG_LZMA2
  109. elif 'lzma3' in mbin:
  110. flags |= FLAG_LZMA3
  111. outdata += struct.pack(
  112. '20sIII', name, address, size, flags)
  113. with open(outfname, 'wb') as fh:
  114. fh.write(outdata)
  115. return
  116. # Find FBD2 description by block device name
  117. def parti_find_fbdev(parti, name):
  118. for desc in parti['descriptions']:
  119. if desc['type'] == 'FBD2' and desc['name'] == name:
  120. return desc
  121. return None
  122. # Copy files, and return files in new directory
  123. def copy_files(dst, fnames):
  124. nnames = []
  125. for f in fnames:
  126. nname = os.path.join(dst, os.path.basename(f))
  127. nnames.append(nname)
  128. shutil.copy(f, nname)
  129. return nnames
  130. # Extract nvid from nvdata, and store to fname
  131. def nvbin_extract(nvdata, eid, fname):
  132. pos = 4
  133. size = len(nvdata)
  134. while pos < size:
  135. nvid, nvsize = struct.unpack('HH', nvdata[pos:pos+4])
  136. if nvid == 0xffff:
  137. break
  138. if nvid == eid:
  139. with open(fname, 'wb') as fh:
  140. fh.write(nvdata[pos+4:pos+4+nvsize])
  141. return True
  142. nvsize = (nvsize + 3) & 0xfffffffc
  143. pos += 4 + nvsize
  144. return False
  145. # Modify piece of data for specified nvid, in nvdata
  146. def nvdata_modify(nvdata, nvid, offset, dlen, ddata):
  147. pos = 4
  148. size = len(nvdata)
  149. while pos < size:
  150. bin_nvid, nvsize = struct.unpack('HH', nvdata[pos:pos+4])
  151. if bin_nvid == 0xffff:
  152. break
  153. if bin_nvid == nvid:
  154. nvdata[pos+4+offset:pos+4+offset+dlen] = ddata
  155. return
  156. nvsize_aligned = (nvsize + 3) & ~3
  157. pos += 4 + nvsize_aligned
  158. # print("nvm nvid {} size {} align {}".format(bin_nvid, nvsize, nvsize_aligned))
  159. raise Exception('nvid {} not found'.format(nvid))
  160. # Apply deltanv.bin to nvdata.
  161. # The format of deltanv.bin is multiple bulks. Each nulk:
  162. # * file name: 20B
  163. # * version: 2B
  164. # * bulk size: 4B (total bulk)
  165. # * multiple bulk data
  166. # * nvid: 2B
  167. # * offset: 2B
  168. # * size: 2B (data size plus 4)
  169. # * data
  170. # * 0xffff: 2B
  171. def nvbin_apply_delta(nvdata, fname):
  172. with open(fname, 'rb') as fh:
  173. delta = fh.read()
  174. size = len(delta)
  175. pos = 0
  176. while pos + 26 < size:
  177. _, dsize = struct.unpack('<HI', delta[pos+20:pos+26])
  178. dpos = pos + 26
  179. while dpos < pos + dsize - 2:
  180. nvid, offset, dlen = struct.unpack('HHH', delta[dpos:dpos+6])
  181. ddata = delta[dpos+6:dpos+6+dlen]
  182. # print("delta nvid {} offset {} len {}, at {}".format(nvid, offset, dlen, dpos))
  183. nvdata_modify(nvdata, nvid, offset, dlen, ddata)
  184. dpos += 6 + dlen
  185. pos = dpos + 2
  186. # Add plain_file to partition
  187. def pgen_add_file(pgen, local_name, fs_name):
  188. if 'plain_file' not in pgen:
  189. pgen['plain_file'] = []
  190. pgen['plain_file'].append({'file': fs_name, 'local_file': local_name})
  191. # Add lzma3_file to partition
  192. def pgen_add_lzma3(pgen, local_name, fs_name):
  193. if 'lzma3_file' not in pgen:
  194. pgen['lzma3_file'] = []
  195. pgen['lzma3_file'].append({'file': fs_name, 'local_file': local_name})
  196. # Add lz4_file to partition
  197. def pgen_add_lz4(pgen, local_name, fs_name):
  198. if 'lz4_file' not in pgen:
  199. pgen['lz4_file'] = []
  200. pgen['lz4_file'].append({'file': fs_name, 'local_file': local_name})
  201. # Write file if changes, data is bytes
  202. def write_file_if_change(fname, data):
  203. if os.path.exists(fname):
  204. with open(fname, 'rb') as fh:
  205. old_data = fh.read()
  206. if old_data == data:
  207. return
  208. ensure_dir(os.path.dirname(fname))
  209. with open(fname, 'wb') as fh:
  210. fh.write(data)
  211. # Load prepack configuration
  212. def prepack_cfg_from_json(cfg, srctop, bintop):
  213. with open(cfg, 'r') as fh:
  214. cfgtxt = fh.read()
  215. cfgdir = os.path.dirname(cfg)
  216. cfgtxt = cfgtxt.replace('@SOURCE_TOP_DIR@', srctop).replace(
  217. '@BINARY_TOP_DIR@', bintop)
  218. pc = json.loads(cfgtxt)
  219. prepackcfg = []
  220. for fm in pc.get('files', []):
  221. rname = fm['file'].replace('\\', '/').replace('//', '/').lstrip('/')
  222. fname = fm['local_file']
  223. if not os.path.isabs(fname):
  224. fname = os.path.join(cfgdir, fname)
  225. prepackcfg.append({'file': rname, 'local_file': fname})
  226. return prepackcfg
  227. # Prepack local files
  228. def file_of_prepack(pc):
  229. return [x['local_file'] for x in pc]
  230. # Create prepack cpio
  231. def create_prepack_cpio(pc, outfname):
  232. with open(outfname, 'wb') as fh:
  233. for fm in pc:
  234. name = fm['file'].replace('\\', '/').replace('//', '/').lstrip('/').encode('utf-8')
  235. name_size = len(name) + 1 # include NUL
  236. name_size_aligned = (name_size + 1) & ~1 # pad to even
  237. file_data = bytes()
  238. with open(fm['local_file'], 'rb') as ffh:
  239. file_data = ffh.read()
  240. file_size = len(file_data)
  241. file_size_aligned = (file_size + 1) & ~1 # pad to even
  242. fstat = os.stat(fm['local_file'])
  243. fh.write(struct.pack(
  244. CPIO_FILE_FORMAT.format(name_size_aligned, file_size_aligned),
  245. CPIO_OLDLE_MAGIC,
  246. fstat.st_dev & 0xffff,
  247. fstat.st_ino & 0xffff,
  248. fstat.st_mode & 0xffff,
  249. fstat.st_uid & 0xffff,
  250. fstat.st_gid & 0xffff,
  251. fstat.st_nlink & 0xffff,
  252. 0, # rdevice_num
  253. int(fstat.st_mtime) >> 16,
  254. int(fstat.st_mtime) & 0xffff,
  255. name_size & 0xffff,
  256. file_size >> 16,
  257. file_size & 0xffff,
  258. name,
  259. file_data))
  260. # When there are no files, write an empty file, and pcgen will
  261. # ignore PREPACK
  262. if len(pc) > 0:
  263. name = b'TRAILER!!!'
  264. name_size = len(name) + 1
  265. name_size_aligned = (name_size + 1) & ~1
  266. fh.write(struct.pack(
  267. CPIO_FILE_FORMAT.format(name_size_aligned, 0),
  268. CPIO_OLDLE_MAGIC,
  269. 0, 0, 0, 0, 0,
  270. 1, 0, 0, 0,
  271. name_size & 0xffff,
  272. 0, 0,
  273. name,
  274. bytes()))
  275. def mimggen8910_args(sub_parser):
  276. parser = sub_parser.add_parser(
  277. 'imggen8910', help='generate dependency rule')
  278. parser.set_defaults(func=mimggen8910)
  279. parser.add_argument('--config', dest='config', required=True,
  280. help='the json file for modem and nvm')
  281. parser.add_argument('--partinfo', dest='partinfo', required=True,
  282. help='the json file for partition')
  283. parser.add_argument('--source-top', dest='srctop', required=True,
  284. help='SOURCE_TOP_DIR, may be used')
  285. parser.add_argument('--binary-top', dest='bintop', required=True,
  286. help='BINARY_TOP_DIR, may be used')
  287. parser.add_argument('--cproot', dest='cproot', required=True,
  288. help='root directory of CP bin and nvm')
  289. parser.add_argument('--aproot', dest='aproot', required=True,
  290. help='root directory of AP nvm')
  291. parser.add_argument('--fix-size', dest='fixsize', default=None,
  292. help='fixnv size')
  293. parser.add_argument('--deltainc', dest='deltainc', default=None,
  294. help='inlcude directory for deltanv')
  295. parser.add_argument('--dep', dest='dep', default=None,
  296. help='output dependency files')
  297. parser.add_argument('--deprel', dest='deprel', default=None,
  298. help='dependency target relative directory')
  299. parser.add_argument('--workdir', dest='workdir', required=True,
  300. help='root directory of AP nvm')
  301. parser.add_argument('--prepackfile', dest='prepackfile', required=True,
  302. help='the json file for prepack')
  303. parser.add_argument('--cpsign', dest='cpsign', action='store_true',
  304. help='sign cp images')
  305. parser.add_argument('--indeltanv', dest='indeltanv', default=None,
  306. help='output indeltanv cpio file')
  307. parser.add_argument('--pn', dest='pn', help='product for vlrsign')
  308. parser.add_argument('--pw', dest='pw', help='password for vlrsign')
  309. parser.add_argument('variant', help='variant name')
  310. parser.add_argument('prepack', help='output prepack cpio file')
  311. parser.add_argument('nvbin', help='output nvitem bin')
  312. parser.add_argument('image', help='output modem image')
  313. def mimggen8910(args):
  314. # Variant description json file name
  315. varjson = os.path.join(args.aproot, 'config', args.variant + '.json')
  316. with open(args.config, 'r') as fh:
  317. cpcfg = json.load(fh)
  318. with open(varjson, 'r') as fh:
  319. vari = json.load(fh)
  320. with open(args.partinfo, 'r') as fh:
  321. parti = json.load(fh)
  322. workdir = args.workdir
  323. cpdir = os.path.join(args.cproot, vari['cpdir'])
  324. apdir = os.path.join(args.aproot, vari['apdir'])
  325. cpdnvdir = os.path.join(cpdir, vari['cpdeltanv'])
  326. # Fixed file name by convention
  327. cpprj = os.path.join(cpdir, 'nvitem/nvitem_modem.prj')
  328. # Fixed file name by convention
  329. apprj = os.path.join(apdir, 'nvitem/nvitem.prj')
  330. dnvin = os.path.join(apdir, 'deltanv/deltanv_all.nv')
  331. cpfilecfg = cpcfg['cpfilelist'][vari['cpfilelist']]
  332. imsdir = os.path.join(cpdir, 'ims_delta_nv')
  333. cpindeltanvdir = os.path.join(cpdir, 'indeltanv')
  334. cpdnvin = os.path.join(cpdnvdir, 'modem_config.nv')
  335. # Collect cp/ap nvm by parsing prj file
  336. cpnvms = nvm_from_prj(cpprj)
  337. apnvms = nvm_from_prj(apprj)
  338. imsnvms = glob.glob(os.path.join(imsdir, '*.nv'))
  339. imsxml = os.path.join(imsdir, 'Index.xml')
  340. indeltanvs = glob.glob(os.path.join(cpindeltanvdir, '*.nv'))
  341. # Collect prepack files
  342. prepack_json = args.prepackfile
  343. prepackcfg = prepack_cfg_from_json(prepack_json, args.srctop, args.bintop)
  344. prepackfiles = file_of_prepack(prepackcfg)
  345. create_prepack_cpio(prepackcfg, args.prepack)
  346. # Detect conflict MODULE in ap/cp
  347. apnvmmodules = [os.path.basename(nvm) for nvm in apnvms]
  348. cpnvmmodules = [os.path.basename(nvm) for nvm in cpnvms]
  349. conflict = [x for x in apnvmmodules if x in cpnvmmodules]
  350. if conflict:
  351. raise Exception('conflict nv modules: ' + conflict)
  352. # Collect cp files
  353. cpfiles = []
  354. for fm in cpfilecfg:
  355. fname = fm.get('local_file', fm['file'])
  356. fname = os.path.join(cpdir, fname)
  357. if fm.get('copy_only', False) and not os.path.exists(fname):
  358. continue
  359. cpfiles.append(fname)
  360. # Output dependency, if changed.
  361. # The dependency should use relative path. Otherwise, ninja will
  362. # always rebuild the target.
  363. if args.dep:
  364. deps = cpfiles + [cpprj] + cpnvms + [apprj] + \
  365. apnvms + [prepack_json] + prepackfiles + \
  366. imsnvms + [imsxml]
  367. if args.indeltanv:
  368. deps.extend(indeltanvs)
  369. deps.append(args.config)
  370. deps.append(args.partinfo)
  371. deps.extend(glob.glob(os.path.join(os.path.dirname(dnvin), '*.nv')))
  372. deps.extend(glob.glob(os.path.join(os.path.dirname(cpdnvin), '*.nv')))
  373. deps.append(__file__)
  374. # HACK: Some special characters can't be handled well by ninja.
  375. # So, ignore them in dep. It will make dependency incomplete,
  376. # and should ignore only if ninja can't handle it.
  377. def dep_ignore(fname):
  378. return '&' in fname
  379. deptarget = args.image
  380. if args.deprel:
  381. deptarget = os.path.relpath(deptarget, args.deprel)
  382. dep = '{}: {}\n'.format(deptarget, ' '.join(
  383. [os.path.relpath(x, args.deprel) for x in deps if not dep_ignore(x)]))
  384. write_file_if_change(args.dep, dep.encode('utf-8'))
  385. # copy cpbins, keep in output directory for easier debug
  386. # memory_index_list.bin will be hacked for flags
  387. cpbindir = os.path.join(workdir, 'cpbin')
  388. ensure_dir(cpbindir)
  389. for f in cpfiles:
  390. cpfile = os.path.basename(f)
  391. new_name = os.path.join(cpbindir, cpfile)
  392. if cpfile == 'memory_index_list.bin':
  393. org_name = new_name + '.org'
  394. shutil.copy(f, org_name)
  395. hack_mem_list(new_name, org_name, cpfilecfg)
  396. elif args.cpsign and cpfile in ['cp.bin', 'zsp.bin', 'bcpu_gsm.bin', 'riscV.bin']:
  397. subprocess.run(['vlrsign', '--pn', args.pn, '--pw', args.pw,
  398. '--ha', 'Blake2', '--img', f, '--out', new_name])
  399. else:
  400. shutil.copy(f, new_name)
  401. # merge ap nvm and cp nvm
  402. nvmdir = os.path.join(workdir, 'nvm')
  403. ensure_dir(nvmdir)
  404. nvmprj = os.path.join(nvmdir, 'nvitem.prj')
  405. nvm_prj_merge(nvmprj, [apprj, cpprj])
  406. nvm_modules = modules_from_prj(nvmprj)
  407. ims_nv_exists = 'ims_nv.nvm' in nvm_modules
  408. # Generate nvitem.bin.
  409. subprocess.run(['NVGen', nvmprj, '-L'])
  410. nvmbin = nvbin_from_prj(nvmprj)
  411. # Read nvitem.bin
  412. with open(nvmbin, 'rb') as fh:
  413. nvmdata = fh.read()
  414. # Padding nvitem.bin
  415. if args.fixsize:
  416. fixsize = int(args.fixsize, 0)
  417. if len(nvmdata) > fixsize:
  418. raise Exception('nvbin exceed fixed size')
  419. nvmdata += b'\xff' * (fixsize - len(nvmdata))
  420. # Generate and apply deltanv. The output file name is fixed by NVGen
  421. dnvdir = os.path.join(workdir, 'deltanv')
  422. dnvfile = os.path.join(dnvdir, 'delta.nv')
  423. dnvbin = os.path.join(dnvdir, 'delta_nv.bin')
  424. ensure_dir(dnvdir)
  425. subprocess.run(['arm-none-eabi-gcc', '-E', '-P', '-x', 'c',
  426. '-I', args.deltainc, dnvin, '-o', dnvfile])
  427. cpdnvin_exists = os.path.exists(cpdnvin)
  428. if cpdnvin_exists:
  429. cpdnvfile = os.path.join(dnvdir, 'cpdelta.nv')
  430. subprocess.run(['arm-none-eabi-gcc', '-E', '-P', '-x', 'c',
  431. '-I', args.deltainc, cpdnvin, '-o', cpdnvfile])
  432. # Write empty Index.xml, to avoid annoying message from NVGen
  433. with open(os.path.join(dnvdir, 'Index.xml'), 'w') as fh:
  434. fh.write('<Operators version="1"/>\n')
  435. # Generate delta_nv.bin use all *.nv under dnvdir, it will merged according to the order of *.nv file name
  436. subprocess.run(['NVGen', nvmprj, '-L', '-c', dnvdir])
  437. nvmdata = bytearray(nvmdata)
  438. nvbin_apply_delta(nvmdata, dnvbin)
  439. # Write nvitem.bin to output
  440. ensure_dir(os.path.dirname(args.nvbin))
  441. with open(args.nvbin, 'wb') as fh:
  442. fh.write(nvmdata)
  443. # Generate inner deltanv use all indelta*.nv under cpindeltanvdir
  444. if args.indeltanv:
  445. dinvdir = os.path.join(workdir, 'indeltanv')
  446. ensure_dir(dinvdir)
  447. invdnvgen = []
  448. for inv in indeltanvs:
  449. invname = os.path.basename(inv)
  450. invname = invname.split('.')[0]
  451. invdir = os.path.join(dinvdir, invname)
  452. ensure_dir(invdir)
  453. subprocess.run(['arm-none-eabi-gcc', '-E', '-P', '-x', 'c',
  454. '-I', args.deltainc, inv, '-o', os.path.join(invdir, os.path.basename(inv))])
  455. subprocess.run(['NVGen', nvmprj, '-L', '-c', invdir])
  456. #stdout=subprocess.DEVNULL)
  457. local = os.path.join('factory',invname+'.bin')
  458. rname = local.replace('\\', '/').replace('//', '/').lstrip('/')
  459. invdnvgen.append({'file': rname,
  460. 'local_file': os.path.join(invdir, 'delta_nv.bin')})
  461. # Write empty Index.xml, to avoid annoying message from NVGen
  462. with open(os.path.join(invdir, 'Index.xml'), 'w') as fh:
  463. fh.write('<Operators version="1"/>\n')
  464. create_prepack_cpio(invdnvgen, args.indeltanv)
  465. # Split nvitem.bin for each nvid
  466. nvbindir = os.path.join(workdir, 'nvbin')
  467. ensure_dir(nvbindir)
  468. nvbins = []
  469. for fm in cpcfg['nvidfilelist']:
  470. nvid = int(fm['nvid'], 0)
  471. fname = os.path.join(nvbindir, fm['file'])
  472. if nvbin_extract(nvmdata, nvid, fname):
  473. nvbins.append(fname)
  474. # modem partition description
  475. pgen = {}
  476. for fm in cpfilecfg:
  477. if fm.get('copy_only', False):
  478. continue
  479. fname = fm['file']
  480. local_fname = fm.get('local_file', fname)
  481. if fm.get('lzma3', False):
  482. pgen_add_lzma3(pgen, os.path.join(cpbindir, local_fname), fname)
  483. else:
  484. pgen_add_file(pgen, os.path.join(cpbindir, local_fname), fname)
  485. for nv in nvbins:
  486. nvbase = os.path.basename(nv)
  487. pgen_add_lz4(pgen, nv, os.path.join(cpcfg['modemnv_dir'], nvbase))
  488. # Generate ims_delta_nv.bin
  489. if ims_nv_exists:
  490. imsgendir = os.path.join(workdir, 'ims_delta_nv')
  491. ensure_dir(imsgendir)
  492. for f in imsnvms:
  493. shutil.copy(f, os.path.join(imsgendir, os.path.basename(f)))
  494. shutil.copy(imsxml, os.path.join(imsgendir, os.path.basename(imsxml)))
  495. subprocess.run(['NVGen', nvmprj, '-L', '-c', imsgendir],
  496. stdout=subprocess.DEVNULL)
  497. pgen_add_lz4(pgen, os.path.join(imsgendir, 'delta_nv.bin'),
  498. 'nvm/ims_delta_nv.bin')
  499. # generate modem image json
  500. fbdevdesc = parti_find_fbdev(parti, cpcfg['fbdev_name'])
  501. mgen = {}
  502. mgen['type'] = fbdevdesc['type']
  503. mgen['offset'] = fbdevdesc['offset']
  504. mgen['size'] = fbdevdesc['size']
  505. mgen['erase_block'] = fbdevdesc['erase_block']
  506. mgen['logic_block'] = fbdevdesc['logic_block']
  507. mgen['partiton'] = []
  508. mgen['partiton'].append(pgen)
  509. mgenfile = os.path.join(workdir, 'modem.json')
  510. with open(mgenfile, 'w') as fh:
  511. json.dump(mgen, fh, indent=4)
  512. # Run flash block device generator
  513. subprocess.run(['dtools', 'fbdevgen', mgenfile, args.image])
  514. return 0
  515. def mnvgen8811_args(sub_parser):
  516. parser = sub_parser.add_parser(
  517. 'nvgen8811', help='generate dependency rule')
  518. parser.set_defaults(func=mnvgen8811)
  519. parser.add_argument('--source-top', dest='srctop', required=True,
  520. help='SOURCE_TOP_DIR, may be used')
  521. parser.add_argument('--binary-top', dest='bintop', required=True,
  522. help='BINARY_TOP_DIR, may be used')
  523. parser.add_argument('--aproot', dest='aproot', required=True,
  524. help='root directory of AP nvm')
  525. parser.add_argument('--fix-size', dest='fixsize', default=None,
  526. help='fixnv size')
  527. parser.add_argument('--deltainc', dest='deltainc', default=None,
  528. help='inlcude directory for deltanv')
  529. parser.add_argument('--dep', dest='dep', default=None,
  530. help='output dependency files')
  531. parser.add_argument('--deprel', dest='deprel', default=None,
  532. help='dependency target relative directory')
  533. parser.add_argument('--workdir', dest='workdir', required=True,
  534. help='root directory of AP nvm')
  535. parser.add_argument('--prepackfile', dest='prepackfile', required=True,
  536. help='the json file for prepack')
  537. parser.add_argument('variant', help='variant name')
  538. parser.add_argument('prepack', help='output prepack cpio file')
  539. parser.add_argument('nvbin', help='output nvitem bin')
  540. def mnvgen8811(args):
  541. workdir = args.workdir
  542. # Fixed file name by convention
  543. apdir = os.path.join(args.aproot, args.variant)
  544. apprj = os.path.join(apdir, 'nvitem/nvitem.prj')
  545. dnvin = os.path.join(apdir, 'deltanv/deltanv_all.nv')
  546. # Collect cp/ap nvm by parsing prj file
  547. apnvms = nvm_from_prj(apprj)
  548. # Collect prepack files
  549. prepack_json = args.prepackfile
  550. prepackcfg = prepack_cfg_from_json(prepack_json, args.srctop, args.bintop)
  551. prepackfiles = file_of_prepack(prepackcfg)
  552. create_prepack_cpio(prepackcfg, args.prepack)
  553. # Output dependency, if changed.
  554. # The dependency should use relative path. Otherwise, ninja will
  555. # always rebuild the target.
  556. if args.dep:
  557. deps = [apprj] + apnvms + [prepack_json] + prepackfiles
  558. deps.extend(glob.glob(os.path.join(os.path.dirname(dnvin), '*.nv')))
  559. deps.append(__file__)
  560. deptarget = args.nvbin
  561. if args.deprel:
  562. deptarget = os.path.relpath(deptarget, args.deprel)
  563. dep = '{}: {}\n'.format(deptarget, ' '.join(
  564. [os.path.relpath(x, args.deprel) for x in deps]))
  565. write_file_if_change(args.dep, dep.encode('utf-8'))
  566. # Merge ap nvm and cp nvm
  567. nvmdir = os.path.join(workdir, 'nvm')
  568. ensure_dir(nvmdir)
  569. nvmprj = os.path.join(nvmdir, 'nvitem.prj')
  570. shutil.copyfile(apprj, nvmprj)
  571. copy_files(nvmdir, apnvms)
  572. # Generate nvitem.bin.
  573. subprocess.run(['NVGen', nvmprj, '-L'])
  574. nvmbin = nvbin_from_prj(nvmprj)
  575. # Read nvitem.bin
  576. with open(nvmbin, 'rb') as fh:
  577. nvmdata = fh.read()
  578. # Padding nvitem.bin
  579. if args.fixsize:
  580. fixsize = int(args.fixsize, 0)
  581. if len(nvmdata) > fixsize:
  582. raise Exception('nvbin exceed fixed size')
  583. nvmdata += b'\xff' * (fixsize - len(nvmdata))
  584. # Generate and apply deltanv. The output file name is fixed by NVGen
  585. dnvdir = os.path.join(workdir, 'deltanv')
  586. dnvfile = os.path.join(dnvdir, 'delta.nv')
  587. dnvbin = os.path.join(dnvdir, 'delta_nv.bin')
  588. ensure_dir(dnvdir)
  589. subprocess.run(['arm-none-eabi-gcc', '-E', '-P', '-x', 'c',
  590. '-I', args.deltainc, dnvin, '-o', dnvfile])
  591. # Write empty Index.xml, to avoid annoying message from NVGen
  592. with open(os.path.join(dnvdir, 'Index.xml'), 'w') as fh:
  593. fh.write('<Operators version="1"/>\n')
  594. subprocess.run(['NVGen', nvmprj, '-L', '-c', dnvdir])
  595. nvmdata = bytearray(nvmdata)
  596. nvbin_apply_delta(nvmdata, dnvbin)
  597. # Write nvitem.bin to output
  598. ensure_dir(os.path.dirname(args.nvbin))
  599. with open(args.nvbin, 'wb') as fh:
  600. fh.write(nvmdata)
  601. return 0
  602. def mvargen8850_args(sub_parser):
  603. parser = sub_parser.add_parser(
  604. 'vargen8850', help='generate modem.cpio and nvitem.bin')
  605. parser.set_defaults(func=mvargen8850)
  606. parser.add_argument('--config', dest='config', required=True,
  607. help='the json file for modem and nvm')
  608. parser.add_argument('--source-top', dest='srctop', required=True,
  609. help='SOURCE_TOP_DIR, may be used')
  610. parser.add_argument('--binary-top', dest='bintop', required=True,
  611. help='BINARY_TOP_DIR, may be used')
  612. parser.add_argument('--cproot', dest='cproot', required=True,
  613. help='root directory of CP bin and nvm')
  614. parser.add_argument('--aproot', dest='aproot', required=True,
  615. help='root directory of AP nvm')
  616. # quectel modify
  617. parser.add_argument('--ims-region', dest='imsregion', required=True,
  618. help='root directory of ims delta nvm')
  619. parser.add_argument('--fix-size', dest='fixsize', default=None,
  620. help='fixnv size')
  621. parser.add_argument('--deltainc', dest='deltainc', default=None,
  622. help='inlcude directory for deltanv')
  623. parser.add_argument('--dep', dest='dep', default=None,
  624. help='output dependency files')
  625. parser.add_argument('--deprel', dest='deprel', default=None,
  626. help='dependency target relative directory')
  627. parser.add_argument('--workdir', dest='workdir', required=True,
  628. help='root directory of AP nvm')
  629. parser.add_argument('--prepackfile', dest='prepackfile', required=True,
  630. help='the json file for prepack')
  631. parser.add_argument('--imsdeltanv', dest='imsdeltanv', default=None,
  632. help='output imsdeltanv cpio file')
  633. parser.add_argument('--indeltanv', dest='indeltanv', default=None,
  634. help='output indeltanv cpio file')
  635. parser.add_argument('--outHexDir', dest='outHexDir', default=None,
  636. help='output out hex dir')
  637. parser.add_argument('--signcpbin', dest='signcpbin', default=None,
  638. help='enable cp bin sign')
  639. parser.add_argument('--simlockeydir', dest='simlockeydir', default=None,
  640. help='assign simlock key directory')
  641. parser.add_argument('variant', help='variant name')
  642. parser.add_argument('prepack', help='output prepack cpio file')
  643. parser.add_argument('nvbin', help='output nvitem bin')
  644. parser.add_argument('modem', help='output modem cpio file')
  645. def mvargen8850(args):
  646. # Variant description json file name
  647. varjson = os.path.join(args.aproot, 'config', args.variant + '.json')
  648. with open(args.config, 'r') as fh:
  649. cpcfg = json.load(fh)
  650. with open(varjson, 'r') as fh:
  651. vari = json.load(fh)
  652. workdir = args.workdir
  653. outHexDir = args.outHexDir
  654. signcpbin = args.signcpbin
  655. simlockeydir = args.simlockeydir
  656. cpdir = os.path.join(args.cproot, vari['cpdir'])
  657. apdir = os.path.join(args.aproot, vari['apdir'])
  658. cpdnvdir = os.path.join(cpdir, vari['cpdeltanv'])
  659. # Fixed file name by convention
  660. cpprj = os.path.join(cpdir, 'nvitem/nvitem_modem.prj')
  661. # Fixed file name by convention
  662. apprj = os.path.join(apdir, 'nvitem/nvitem.prj')
  663. dnvin = os.path.join(apdir, 'deltanv/deltanv_all.nv')
  664. cpfilecfg = cpcfg['cpfilelist'][vari['cpfilelist']]
  665. lzma_block_size = cpcfg['lzma_block_size']
  666. # quectel modify
  667. imsdir = os.path.join(args.imsregion, 'ims_delta_nv')
  668. cpindeltanvdir = os.path.join(cpdir, 'indeltanv')
  669. cpdnvin = os.path.join(cpdnvdir, 'modem_config.nv')
  670. # Collect cp/ap nvm by parsing prj file
  671. cpnvms = nvm_from_prj(cpprj)
  672. apnvms = nvm_from_prj(apprj)
  673. imsnvms = glob.glob(os.path.join(imsdir, '*.nv'))
  674. imsxml = os.path.join(imsdir, 'Index.xml')
  675. indeltanvs = glob.glob(os.path.join(cpindeltanvdir, '*.nv'))
  676. # Collect prepack files
  677. prepack_json = args.prepackfile
  678. prepackcfg = prepack_cfg_from_json(prepack_json, args.srctop, args.bintop)
  679. prepackfiles = file_of_prepack(prepackcfg)
  680. create_prepack_cpio(prepackcfg, args.prepack)
  681. # Detect conflict MODULE in ap/cp
  682. apnvmmodules = [os.path.basename(nvm) for nvm in apnvms]
  683. cpnvmmodules = [os.path.basename(nvm) for nvm in cpnvms]
  684. conflict = [x for x in apnvmmodules if x in cpnvmmodules]
  685. if conflict:
  686. raise Exception('conflict nv modules: ' + conflict)
  687. # Collect cp files
  688. cpfiles = []
  689. for fm in cpfilecfg:
  690. fname = fm.get('local_file', fm['file'])
  691. fname = os.path.join(cpdir, fname)
  692. if fm.get('copy_only', False) and not os.path.exists(fname):
  693. continue
  694. cpfiles.append(fname)
  695. # Output dependency, if changed.
  696. # The dependency should use relative path. Otherwise, ninja will
  697. # always rebuild the target.
  698. if args.dep:
  699. deps = cpfiles + [cpprj] + cpnvms + [apprj] + \
  700. apnvms + [prepack_json] + prepackfiles
  701. if args.imsdeltanv:
  702. deps.extend(imsnvms)
  703. deps.append(imsxml)
  704. if args.indeltanv:
  705. deps.extend(indeltanvs)
  706. deps.append(args.config)
  707. deps.extend(glob.glob(os.path.join(os.path.dirname(dnvin), '*.nv')))
  708. deps.extend(glob.glob(os.path.join(os.path.dirname(cpdnvin), '*.nv')))
  709. deps.append(__file__)
  710. # HACK: Some special characters can't be handled well by ninja.
  711. # So, ignore them in dep. It will make dependency incomplete,
  712. # and should ignore only if ninja can't handle it.
  713. def dep_ignore(fname):
  714. return '&' in fname
  715. deptarget = args.nvbin
  716. if args.deprel:
  717. deptarget = os.path.relpath(deptarget, args.deprel)
  718. dep = '{}: {}\n'.format(deptarget, ' '.join(
  719. [os.path.relpath(x, args.deprel) for x in deps if not dep_ignore(x)]))
  720. write_file_if_change(args.dep, dep.encode('utf-8'))
  721. # copy cpbins, keep in output directory for easier debug
  722. # memory_index_list.bin will be hacked for flags
  723. cpbindir = os.path.join(workdir, 'cpbin')
  724. ensure_dir(cpbindir)
  725. for f in cpfiles:
  726. cpfile = os.path.basename(f)
  727. new_name = os.path.join(cpbindir, cpfile)
  728. if cpfile == 'memory_index_list.bin':
  729. org_name = new_name + '.org'
  730. shutil.copy(f, org_name)
  731. hack_mem_list(new_name, org_name, cpfilecfg)
  732. elif cpfile == 'cp.bin':
  733. if simlockeydir != 'NULL':
  734. os.system('python ' +os.path.join(sys.path[0],'SimlockInsKey.py')+ ' '+ f + ' '+ simlockeydir)
  735. if signcpbin == 'true':
  736. cpsignfile = os.path.join(outHexDir, 'cp-sign.img')
  737. print ("Do cpio file with: [cp-sign.img]")
  738. shutil.copy(cpsignfile, new_name)
  739. else:
  740. print("Do cpio file with [" + cpfile + "]")
  741. shutil.copy(f, new_name)
  742. else:
  743. shutil.copy(f, new_name)
  744. # merge ap nvm and cp nvm
  745. nvmdir = os.path.join(workdir, 'nvm')
  746. ensure_dir(nvmdir)
  747. nvmprj = os.path.join(nvmdir, 'nvitem.prj')
  748. nvm_prj_merge(nvmprj, [apprj, cpprj])
  749. # Generate nvitem.bin.
  750. subprocess.run(['NVGen', nvmprj, '-L'])
  751. nvmbin = nvbin_from_prj(nvmprj)
  752. # Read nvitem.bin
  753. with open(nvmbin, 'rb') as fh:
  754. nvmdata = fh.read()
  755. # Padding nvitem.bin
  756. if args.fixsize:
  757. fixsize = int(args.fixsize, 0)
  758. if len(nvmdata) > fixsize:
  759. raise Exception('nvbin exceed fixed size')
  760. nvmdata += b'\xff' * (fixsize - len(nvmdata))
  761. # Generate and apply deltanv. The output file name is fixed by NVGen
  762. dnvdir = os.path.join(workdir, 'deltanv')
  763. dnvfile = os.path.join(dnvdir, 'delta.nv')
  764. dnvbin = os.path.join(dnvdir, 'delta_nv.bin')
  765. ensure_dir(dnvdir)
  766. subprocess.run(['arm-none-eabi-gcc', '-E', '-P', '-x', 'c',
  767. '-I', args.deltainc, dnvin, '-o', dnvfile])
  768. cpdnvin_exists = os.path.exists(cpdnvin)
  769. if cpdnvin_exists:
  770. cpdnvfile = os.path.join(dnvdir, 'cpdelta.nv')
  771. subprocess.run(['arm-none-eabi-gcc', '-E', '-P', '-x', 'c',
  772. '-I', args.deltainc, cpdnvin, '-o', cpdnvfile])
  773. # Write empty Index.xml, to avoid annoying message from NVGen
  774. with open(os.path.join(dnvdir, 'Index.xml'), 'w') as fh:
  775. fh.write('<Operators version="1"/>\n')
  776. # Generate delta_nv.bin use all *.nv under dnvdir, it will merged
  777. # according to the order of *.nv file name
  778. subprocess.run(['NVGen', nvmprj, '-L', '-c', dnvdir])
  779. nvmdata = bytearray(nvmdata)
  780. nvbin_apply_delta(nvmdata, dnvbin)
  781. # Write nvitem.bin to output
  782. ensure_dir(os.path.dirname(args.nvbin))
  783. with open(args.nvbin, 'wb') as fh:
  784. fh.write(nvmdata)
  785. # Generate inner deltanv use all indelta*.nv under cpindeltanvdir
  786. if args.indeltanv:
  787. dinvdir = os.path.join(workdir, 'indeltanv')
  788. ensure_dir(dinvdir)
  789. invdnvgen = []
  790. for inv in indeltanvs:
  791. invname = os.path.basename(inv)
  792. invname = invname.split('.')[0]
  793. invdir = os.path.join(dinvdir, invname)
  794. ensure_dir(invdir)
  795. subprocess.run(['arm-none-eabi-gcc', '-E', '-P', '-x', 'c',
  796. '-I', args.deltainc, inv, '-o', os.path.join(invdir, os.path.basename(inv))])
  797. subprocess.run(['NVGen', nvmprj, '-L', '-c', invdir])
  798. #stdout=subprocess.DEVNULL)
  799. local = os.path.join('factory',invname+'.bin')
  800. rname = local.replace('\\', '/').replace('//', '/').lstrip('/')
  801. invdnvgen.append({'file': rname,
  802. 'local_file': os.path.join(invdir, 'delta_nv.bin')})
  803. # Write empty Index.xml, to avoid annoying message from NVGen
  804. with open(os.path.join(invdir, 'Index.xml'), 'w') as fh:
  805. fh.write('<Operators version="1"/>\n')
  806. create_prepack_cpio(invdnvgen, args.indeltanv)
  807. # modem cpio configuration
  808. cpgen = []
  809. for fm in cpfilecfg:
  810. if fm.get('copy_only', False):
  811. continue
  812. fname = os.path.join('modem', fm['file'])
  813. local_fname = os.path.join(cpbindir, fm.get('local_file', fm['file']))
  814. if fm.get('lzma3', False):
  815. for bfname in glob.glob(local_fname + '.???'):
  816. os.unlink(bfname)
  817. subprocess.run(['dtools', 'lzmare3', '--block-size', lzma_block_size,
  818. lzma_block_size, local_fname, local_fname])
  819. suffixes = [x.split('.')[-1]
  820. for x in glob.glob(local_fname + '.???')]
  821. for s in sorted(suffixes):
  822. cpgen.append({'file': '{}.{}'.format(fname, s),
  823. 'local_file': '{}.{}'.format(local_fname, s)})
  824. else:
  825. cpgen.append({'file': fname, 'local_file': local_fname})
  826. # generate modem image json
  827. create_prepack_cpio(cpgen, args.modem)
  828. # Generate ims_delta_nv.bin
  829. if args.imsdeltanv:
  830. imsgendir = os.path.join(workdir, 'ims_delta_nv')
  831. ensure_dir(imsgendir)
  832. for f in imsnvms:
  833. shutil.copy(f, os.path.join(imsgendir, os.path.basename(f)))
  834. shutil.copy(imsxml, os.path.join(imsgendir, os.path.basename(imsxml)))
  835. subprocess.run(['NVGen', nvmprj, '-L', '-c', imsgendir],
  836. stdout=subprocess.DEVNULL)
  837. imsdnvgen = []
  838. imsdnvgen.append({'file': '/factory/ims_delta_nv.bin',
  839. 'local_file': os.path.join(imsgendir, 'delta_nv.bin')})
  840. create_prepack_cpio(imsdnvgen, args.imsdeltanv)
  841. return 0
  842. def mprepack_args(sub_parser):
  843. parser = sub_parser.add_parser(
  844. 'prepackgen', help='generate prepack cpio')
  845. parser.set_defaults(func=mprepackgen)
  846. parser.add_argument('--source-top', dest='srctop', required=True,
  847. help='SOURCE_TOP_DIR, may be used')
  848. parser.add_argument('--binary-top', dest='bintop', required=True,
  849. help='BINARY_TOP_DIR, may be used')
  850. parser.add_argument('--prepackfile', dest='prepackfile', required=True,
  851. help='the json file for prepack')
  852. parser.add_argument('prepack', help='output prepack cpio file')
  853. def mprepackgen(args):
  854. prepack_json = args.prepackfile
  855. prepackcfg = prepack_cfg_from_json(prepack_json, args.srctop, args.bintop)
  856. prepackfiles = file_of_prepack(prepackcfg)
  857. create_prepack_cpio(prepackcfg, args.prepack)
  858. def main(argv):
  859. parser = argparse.ArgumentParser(description=DESCRIPTION,
  860. formatter_class=argparse.RawTextHelpFormatter)
  861. sub_parser = parser.add_subparsers(help='sub-commnads')
  862. mimggen8910_args(sub_parser)
  863. mnvgen8811_args(sub_parser)
  864. mvargen8850_args(sub_parser)
  865. mprepack_args(sub_parser)
  866. args = parser.parse_args(argv)
  867. if args.__contains__('func'):
  868. return args.func(args)
  869. parser.parse_args(['-h'])
  870. return 0
  871. if __name__ == "__main__":
  872. sys.exit(main(sys.argv[1:]))