ramdump.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546
  1. import struct
  2. import sys
  3. import os
  4. import time
  5. import re
  6. from ctypes import *
  7. class RamdumpHead(Structure):
  8. _fields_ = [
  9. ("magic", c_uint32),
  10. ("version", c_uint16),
  11. ("hdr_sz", c_uint16),
  12. ("img_sz", c_uint32),
  13. ("org_sz", c_uint32),
  14. ("datetime", c_uint8 * 16),
  15. ("inc_uid", c_uint16),
  16. ("cpu_id", c_uint16),
  17. ("reserved", c_uint8 * 4),
  18. ("img_chksum", c_uint32),
  19. ("hdr_chksum", c_uint32),
  20. ]
  21. class RamdumpRegionHead(Structure):
  22. _fields_ = [
  23. ("magic", c_uint32),
  24. ("type", c_uint16),
  25. ("hdr_sz", c_uint16),
  26. ("img_sz", c_uint32),
  27. ("org_sz", c_uint32),
  28. ("address", c_uint32),
  29. ("reserved", c_uint8 * 12),
  30. ]
  31. class CompressHead(Structure):
  32. _fields_ = [
  33. ("algo_id", c_uint32),
  34. ("hdr_size", c_uint32),
  35. ("img_size", c_uint32),
  36. ("org_size", c_uint32),
  37. ]
  38. class McpuDebug(Structure):
  39. _fields_ = [
  40. ("r", c_uint32 * 16),
  41. ("xpsr", c_uint32),
  42. ("msp", c_uint32),
  43. ("psp", c_uint32),
  44. ("exc_ret", c_uint32),
  45. ("control", c_uint32),
  46. ("reserved", c_uint32 * 11),
  47. ]
  48. class BtcpuDebug(Structure):
  49. _fields_ = [
  50. ("bt_info", c_uint8 * 128),
  51. ]
  52. class RamdumpAddr(Structure):
  53. _fields_ = [
  54. ("start", c_int32),
  55. ("next", c_int32),
  56. ("filter", c_int32),
  57. ]
  58. RAMD_FLASH_BLK_SZ = 4 * 1024
  59. RAMD_COMPR_BLK_SZ = 32 * 1024
  60. RAMD_VERSION = 0x0001
  61. MAGIC_RAMD = 0x444d4152
  62. MAGIC_RAMR = 0x524d4152
  63. MAGIC_LZ4 = 0x20345a4c
  64. MAGIC_FLZ = 0x205a4c46
  65. COMPR_NULL = 0,
  66. COMPR_RLE = 0x20454c52
  67. COMPR_BZ3 = 0x20335a42
  68. COMPR_QLZ = 0x205a4c51
  69. COMPR_LZ4 = 0x20345a4c
  70. COMPR_FL2 = 0x20324c46
  71. COMPR_XZ = 0x414d5a4c
  72. COMPR_BROT = 0x544f5242
  73. COMPR_ZSTD = 0x4454535a
  74. COMPR_FLZ = 0x205a4c46
  75. COMPR_MLZ = 0x205a4c4d
  76. COMPR_HTSH = 0x48535448
  77. COMPR_SHOC = 0x434f4853
  78. COMPR_SMAZ = 0x5a414d53
  79. COMPR_UNIS = 0x53494e55
  80. TYPE_ADDR = 0
  81. TYPE_MCPU_DBG = 1
  82. TYPE_BTCPU_DBG = 2
  83. CORTEX_M4 = 0xc24
  84. CORTEX_M33 = 0x132
  85. strOnProjectLoadStart = \
  86. "void OnProjectLoad(void) {\r\n" \
  87. " Debug.SetConnectMode(CM_ATTACH);\r\n" \
  88. " Debug.SetResetMode(RM_BREAK_AT_SYMBOL);\r\n" \
  89. " Project.SetHostIF(\"USB\", \"\");\r\n" \
  90. " Project.SetTargetIF(\"SWD\");\r\n" \
  91. " Project.SetTIFSpeed(\"4 MHz\");\r\n" \
  92. " //Project.SetOSPlugin(\"ZephyrPlugin_CM4\");\r\n"
  93. strOnProjectLoadEnd = \
  94. " Project.SetDevice(\"%s\");\r\n" \
  95. " Project.AddSvdFile(\"$(InstallDir)/Config/CPU/%sF.svd\");\r\n" \
  96. " File.Open(\"$(ProjectDir)/%s\");\r\n" \
  97. "}\r\n\r\n"
  98. strOnStartupComplete = \
  99. "void OnStartupComplete(void) {\r\n" \
  100. " LoadRamDump();\r\n" \
  101. "}\r\n\r\n"
  102. strAfterTargetReset = \
  103. "void AfterTargetReset(void) {\r\n" \
  104. " Target.SetReg(\"SP\", Target.ReadU32(0x0));\r\n" \
  105. " Target.SetReg(\"PC\", Target.ReadU32(0x4));\r\n" \
  106. "}\r\n\r\n"
  107. strRamDumpStart = \
  108. "void LoadRamDump(void) {\r\n" \
  109. " Target.WriteU32(0x40018004, 0x9);\r\n"
  110. strRamDumpLoadMem = \
  111. " Target.LoadMemory(\"0x%08x.bin\", 0x%08x);\r\n"
  112. strRamDumpSetRegIdx = \
  113. " Target.SetReg(\"R%d\", 0x%08x);\r\n"
  114. strRamDumpSetRegStr = \
  115. " Target.SetReg(\"%s\", 0x%08x);\r\n"
  116. strRamDumpSetPC = \
  117. " Debug.SetNextPC(0x%08x);\r\n" \
  118. " Show.PC();\r\n" \
  119. " Show.Memory(0x%08x);\r\n"
  120. strRamDumpEnd = \
  121. "}\r\n\r\n"
  122. strZephyrElf = "../../zephyr.elf"
  123. iodev_addr = RamdumpAddr()
  124. iodev_addr.start = 0x40000000
  125. iodev_addr.next = 0x50000000
  126. iodev_addr.filter = 0
  127. s_crc32 = [
  128. 0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
  129. 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
  130. 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c,
  131. 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c
  132. ]
  133. def utils_crc32(crc, data):
  134. crcu32 = crc
  135. crcu32 = ~crcu32 & 0xFFFFFFFF
  136. for b in data:
  137. crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b & 0xF)]
  138. crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b >> 4)]
  139. return ~crcu32 & 0xFFFFFFFF
  140. def print_usage(argv0):
  141. print("Usage:")
  142. # print(" -c ramdump compress")
  143. print(" -d ramdump decompress")
  144. print(" -o output file/dir")
  145. print("\nExample:")
  146. # print(" %s -c in-dir -o out-file" % argv0)
  147. print(" %s -d in-file -o out-dir" % argv0)
  148. def process_argvs():
  149. is_compress = None
  150. in_file = None
  151. out_file = None
  152. for i in range(len(sys.argv)):
  153. if sys.argv[i] == "-c":
  154. is_compress = True
  155. in_file = sys.argv[i + 1]
  156. elif sys.argv[i] == "-d":
  157. is_compress = False
  158. in_file = sys.argv[i + 1]
  159. elif sys.argv[i] == "-o":
  160. out_file = sys.argv[i + 1]
  161. return is_compress, in_file, out_file
  162. def init_decompress_ramdump_head(src):
  163. ramdump_head = RamdumpHead()
  164. memmove(addressof(ramdump_head), src, sizeof(RamdumpHead))
  165. if ramdump_head.magic != MAGIC_RAMD:
  166. return None
  167. checksum = utils_crc32(0, src[:ramdump_head.hdr_sz - 4])
  168. if ramdump_head.hdr_chksum != checksum:
  169. print("verify header failed");
  170. return None
  171. if ramdump_head.hdr_sz + ramdump_head.img_sz <= len(src):
  172. checksum = utils_crc32(0, src[ramdump_head.hdr_sz: ramdump_head.hdr_sz + ramdump_head.img_sz])
  173. if ramdump_head.img_chksum != checksum:
  174. print("verify image failed");
  175. return None
  176. else:
  177. print("The file is not complete!");
  178. return ramdump_head
  179. def ramdump_dir_list(folder):
  180. dir_list = []
  181. for file in os.listdir(folder):
  182. if not os.path.isdir(os.path.join(folder, file)):
  183. continue
  184. pattern = r"\d{8}-\d{6}-\d{2}"
  185. if re.match(pattern, file):
  186. dir_list.append(os.path.join(folder, file))
  187. return dir_list
  188. def ramdum_file_list(folder):
  189. file_list = []
  190. for file in os.listdir(folder):
  191. if os.path.isfile(os.path.join(folder, file)):
  192. file_list.append(os.path.join(folder, file))
  193. return file_list
  194. def ramdump_compress_file(in_dir, out_file):
  195. pass
  196. def round_up(x, a):
  197. return (x + (a - 1)) & ~(a - 1)
  198. def fastlz1_decompress(compr_data):
  199. raw_data = bytes()
  200. compr_idx = 0
  201. while compr_idx < len(compr_data):
  202. ctrl = compr_data[compr_idx]
  203. if compr_idx == 0:
  204. ctrl &= 31
  205. compr_idx += 1
  206. if ctrl >= 32:
  207. lens = (ctrl >> 5) - 1
  208. ofs = (ctrl & 31) << 8
  209. ref_idx = len(raw_data) - ofs - 1
  210. if lens == 6:
  211. lens += compr_data[compr_idx]
  212. compr_idx += 1
  213. ref_idx -= compr_data[compr_idx]
  214. compr_idx += 1
  215. lens += 3
  216. if ref_idx < 0:
  217. return b''
  218. while lens > 0:
  219. mlen = lens
  220. if ref_idx + lens > len(raw_data):
  221. mlen = len(raw_data) - ref_idx
  222. raw_data += raw_data[ref_idx: ref_idx + mlen]
  223. ref_idx += mlen
  224. lens -= mlen
  225. else:
  226. ctrl += 1
  227. if compr_idx + ctrl > len(compr_data):
  228. return b''
  229. raw_data += compr_data[compr_idx: compr_idx + ctrl]
  230. compr_idx += ctrl
  231. return raw_data
  232. def __decompress_single(algo, src):
  233. dst = bytes()
  234. if algo == COMPR_FLZ:
  235. dst = fastlz1_decompress(src)
  236. return dst
  237. def __decompress(algo, src):
  238. src_length = len(src)
  239. dst = bytes()
  240. offset = 0
  241. while src_length > 0:
  242. compress_head = CompressHead()
  243. memmove(addressof(compress_head), src[offset:], sizeof(CompressHead))
  244. offset += sizeof(CompressHead)
  245. src_length -= sizeof(CompressHead)
  246. if compress_head.hdr_size != sizeof(CompressHead):
  247. return dst
  248. in_size = compress_head.img_size
  249. sub_dst = __decompress_single(compress_head.algo_id, src[offset:offset + in_size])
  250. if len(sub_dst) != compress_head.org_size:
  251. return dst
  252. in_size = round_up(in_size, 4)
  253. offset += in_size
  254. src_length -= in_size
  255. dst += sub_dst
  256. return dst
  257. def ramdump_decompress(ramdump_src):
  258. if len(ramdump_src) < sizeof(RamdumpRegionHead):
  259. return None
  260. region_head = RamdumpRegionHead()
  261. memmove(addressof(region_head), ramdump_src, sizeof(RamdumpRegionHead))
  262. dst = __decompress(COMPR_FLZ,
  263. ramdump_src[sizeof(RamdumpRegionHead):region_head.img_sz + sizeof(RamdumpRegionHead)])
  264. if len(dst) <= 0:
  265. return region_head, None
  266. return region_head, dst
  267. def get_file_info(ramdump_type, address, addr_file_list, dst):
  268. mcpu_debug = None
  269. btcpu_debug = None
  270. file_name = str()
  271. if ramdump_type == TYPE_MCPU_DBG:
  272. file_name = 'dbg_mcpu.bin'
  273. mcpu_debug = McpuDebug()
  274. memmove(addressof(mcpu_debug), dst, sizeof(McpuDebug))
  275. elif ramdump_type == TYPE_BTCPU_DBG:
  276. file_name = 'dbg_btcpu.bin'
  277. btcpu_debug = BtcpuDebug()
  278. memmove(addressof(btcpu_debug), dst, sizeof(BtcpuDebug))
  279. elif ramdump_type == TYPE_ADDR:
  280. file_name = '0x%08x.bin' % address
  281. addr_file_list.append(address)
  282. return file_name, mcpu_debug, btcpu_debug
  283. def out_ramdump_decompress_files(ramdump_src, ramdump_out_dir, addr_file_list):
  284. sub_offset = 0
  285. out_size = 0
  286. mcpu_debug = None
  287. btcpu_debug = None
  288. while True:
  289. if sub_offset > len(ramdump_src) - 1:
  290. break
  291. region_head, dst = ramdump_decompress(ramdump_src[sub_offset:])
  292. if dst is None:
  293. break
  294. sub_offset += region_head.hdr_sz
  295. sub_offset += region_head.img_sz
  296. out_size += len(dst)
  297. file_name, mcpu_debug1, btcpu_debug1 = get_file_info(region_head.type,
  298. region_head.address,
  299. addr_file_list,
  300. dst)
  301. if mcpu_debug1 is not None:
  302. mcpu_debug = mcpu_debug1
  303. if btcpu_debug1 is not None:
  304. btcpu_debug = btcpu_debug1
  305. print(" name: %15s, type: %d, address: 0x%08x, size: %d, img_sz: %d"
  306. % (file_name, region_head.type, region_head.address, len(dst), region_head.img_sz))
  307. with open(ramdump_out_dir + '\\' + file_name, "wb") as out_file:
  308. out_file.write(dst)
  309. return out_size, mcpu_debug, btcpu_debug
  310. def build_ozone_jdebug(cpu_id, addr_file_list, mcpu_debug, btcpu_debug):
  311. cup_str = str()
  312. if cpu_id == CORTEX_M4:
  313. cup_str = 'Cortex-M4'
  314. elif cpu_id == CORTEX_M33:
  315. cup_str = 'Cortex-M33'
  316. dst = strOnProjectLoadStart.encode()
  317. cmd_str = strOnProjectLoadEnd % (cup_str, cup_str, strZephyrElf)
  318. dst += cmd_str.encode()
  319. dst += strOnStartupComplete.encode()
  320. dst += strAfterTargetReset.encode()
  321. dst += strRamDumpStart.encode()
  322. for addr in addr_file_list:
  323. if iodev_addr.start <= addr < iodev_addr.next:
  324. continue
  325. cmd_str = strRamDumpLoadMem % (addr, addr)
  326. dst += cmd_str.encode()
  327. if mcpu_debug is not None:
  328. for i in range(len(mcpu_debug.r)):
  329. cmd_str = strRamDumpSetRegIdx % (i, mcpu_debug.r[i])
  330. dst += cmd_str.encode()
  331. if cpu_id != CORTEX_M33:
  332. cmd_str = strRamDumpSetRegStr % ('MSP', mcpu_debug.msp)
  333. dst += cmd_str.encode()
  334. cmd_str = strRamDumpSetRegStr % ('PSP', mcpu_debug.psp)
  335. dst += cmd_str.encode()
  336. cmd_str = strRamDumpSetRegStr % ('XPSR', mcpu_debug.xpsr)
  337. dst += cmd_str.encode()
  338. cmd_str = strRamDumpSetRegStr % ('Control', (mcpu_debug.control << 24))
  339. dst += cmd_str.encode()
  340. cmd_str = strRamDumpSetPC % (mcpu_debug.r[15], mcpu_debug.r[13])
  341. dst += cmd_str.encode()
  342. dst += strRamDumpEnd.encode()
  343. return dst
  344. def step_offset(offset):
  345. if offset % RAMD_FLASH_BLK_SZ:
  346. offset = round_up(offset, RAMD_FLASH_BLK_SZ)
  347. else:
  348. offset += RAMD_FLASH_BLK_SZ
  349. return offset
  350. def check_ramdump_head(src):
  351. offset = 0
  352. ramdump_head = None
  353. while offset < len(src):
  354. ramdump_head = init_decompress_ramdump_head(src[offset:])
  355. if ramdump_head is not None:
  356. break
  357. else:
  358. offset = step_offset(offset)
  359. if offset >= len(src):
  360. return -1, None
  361. return offset, ramdump_head
  362. def ramdump_decompress_file(in_file, out_dir):
  363. os.makedirs(out_dir, exist_ok=True)
  364. f = open(in_file, "rb")
  365. src = f.read()
  366. src_length = len(src)
  367. f.close()
  368. if src_length <= 0:
  369. print("Read %s size 0" % in_file)
  370. return -4
  371. offset, _ = check_ramdump_head(src)
  372. if offset < 0 or offset >= src_length:
  373. return -5
  374. src = src[offset:] + src[:offset]
  375. start_offset = offset
  376. offset = 0
  377. while offset < src_length:
  378. addr_file_list = []
  379. ramdump_head = init_decompress_ramdump_head(src[offset:])
  380. if ramdump_head is None:
  381. offset = step_offset(offset)
  382. continue
  383. in_size = ramdump_head.hdr_sz + ramdump_head.img_sz
  384. ramdump_src = src[offset + ramdump_head.hdr_sz:offset + in_size]
  385. ramdump_out_dir = "{}\\{}-{}".format(out_dir, bytes(ramdump_head.datetime[:-1]).decode(), ramdump_head.inc_uid)
  386. os.makedirs(ramdump_out_dir, exist_ok=True)
  387. print("[ramdump v%d] %s org_sz=%d, img_sz = %d, uid = %d, offset = 0x%x." % (ramdump_head.version,
  388. bytes(ramdump_head.datetime[
  389. :-1]).decode(),
  390. ramdump_head.org_sz,
  391. ramdump_head.img_sz,
  392. ramdump_head.inc_uid,
  393. start_offset + offset))
  394. print("Output dir: %s." % ramdump_out_dir)
  395. out_size, mcpu_debug, btcpu_debug = out_ramdump_decompress_files(ramdump_src, ramdump_out_dir, addr_file_list)
  396. offset += in_size
  397. print(" Decompressed %d -> %d (%d%%)" % (in_size, out_size, in_size * 100 / out_size))
  398. print("[Ozone] Build ozone.jdebug\r\n")
  399. dst = build_ozone_jdebug(ramdump_head.cpu_id, addr_file_list, mcpu_debug, btcpu_debug)
  400. with open(ramdump_out_dir + '\\' + 'ozone.jdebug', "wb") as out_file:
  401. out_file.write(dst)
  402. def main():
  403. argvs = len(sys.argv)
  404. if argvs < 5:
  405. print_usage(sys.argv[0])
  406. return -1
  407. is_compress, in_argv, out_argv = process_argvs()
  408. if not in_argv or not out_argv:
  409. print_usage(sys.argv[0])
  410. return -2
  411. if is_compress:
  412. ramdump_compress_file(in_argv, out_argv)
  413. else:
  414. ramdump_decompress_file(in_argv, out_argv)
  415. if __name__ == '__main__':
  416. start_time = time.time() * 1000
  417. main()
  418. end_time = time.time() * 1000
  419. print("Consume:", end_time - start_time, "ms.")